POJ-3693 Maximum repetition substring (后缀数组)

POJ-3693 Maximum repetition substring (后缀数组)

题意:求连续重复次数最多的子串

一个连续重复子串可以描述为\(l,r,len\)(端点,循环节长度)

\(O(n^2)\):直接枚举\(l,len\),然后求\(l\)\(l+len\)\(LCP\),就能得到最大的\(r\)

考虑不一一枚举\(l\)

枚举\(len\)之后,从一个点开始向前取\(LCP\),向后取\(LCP\),就能够拼出一个完整的循环串

用这种方法可以去掉大量重复枚举,只枚举\(l=k\cdot len (k\in Z)\)

用ST表查询可以做到\(O(n \ln n)\)

细节似乎蛮多的,代码很烦

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cctype>
#include<cstring>
#include<cassert>
using namespace std;

namespace Folder{
#define reg register
#define pb push_back
    typedef long long ll;
    typedef unsigned long long ull;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
    char IO;
    //template <class T=int> 
    int rd(){
        int s=0;
        int f=0;
        while(!isdigit(IO=getchar())) f|=(IO=='-');
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
}
using namespace Folder;


const int N=1e5+10,INF=1e9;
int n;
char s[N];
int kase;
#define ms(a) memset(a,0,sizeof a)
struct Sparse_Table{
    int s[20][N],Log[N];
    void PreMake(const int *a){ 
        rep(i,2,n) Log[i]=Log[i>>1]+1;
        rep(i,1,n) s[0][i]=a[i];
        rep(i,1,Log[n]) {
            int len=(1<<(i-1));
            rep(j,1,n-(1<<i)+1) s[i][j]=min(s[i-1][j],s[i-1][j+len]);
        }
    }
    int Que(int l,int r) {
        int d=Log[r-l+1];
        return min(s[d][l],s[d][r-(1<<d)+1]);
    }
};

struct Suffix_Array{
    int cnt[N],rk[N<<1],sa[N],tmp[N],lcp[N];
    Sparse_Table ST;
    void PreMake(char *s){
        n=strlen(s+1);
        memset(cnt,0,800);
        rep(i,1,n) cnt[(int)s[i]]++;
        rep(i,1,200) cnt[i]+=cnt[i-1];
        rep(i,1,n) rk[i]=cnt[(int)s[i]],sa[i]=i;
        rep(i,n+1,n*2) rk[i]=0;
        for(reg int k=1;k<=n;k<<=1) {
            rep(i,0,n) cnt[i]=0;
            rep(i,1,n) cnt[rk[i+k]]++;
            rep(i,1,n) cnt[i]+=cnt[i-1];
            drep(i,n,1) tmp[cnt[rk[i+k]]--]=i;

            rep(i,0,n) cnt[i]=0;
            rep(i,1,n) cnt[rk[i]]++;
            rep(i,1,n) cnt[i]+=cnt[i-1];
            drep(i,n,1) sa[cnt[rk[tmp[i]]]--]=tmp[i];

            rep(i,1,n) tmp[sa[i]]=tmp[sa[i-1]]+(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+k]!=rk[sa[i-1]+k]);
            rep(i,1,n) rk[i]=tmp[i];
        }
        int h=0;
        memset(lcp,0,(n+2)*4);
        rep(i,1,n) {
            int j=sa[rk[i]-1];
            if(h) h--;
            while(i+h<=n && j+h<=n && s[i+h]==s[j+h]) h++;
            lcp[rk[i]-1]=h;
        }
        ST.PreMake(lcp);
    }
    int Que(int x,int y){
        if(x==y) return n-x+1;
        if(rk[x]>rk[y]) swap(x,y);
        return ST.Que(rk[x],rk[y]-1);
    }
}SA,RSA;
Sparse_Table ST;


int main(){
    while(~scanf("%s",s+1) && s[1]!='#') {
        n=strlen(s+1);
        SA.PreMake(s),reverse(s+1,s+n+1),RSA.PreMake(s);
        ST.PreMake(SA.rk);
        int ans=0,st,ed;
        rep(i,1,n) {
            rep(j,1,n) {
                int a=i*(j-1)+1,b=i*j+1;
                if(b>n) break;
                int len=SA.Que(a,b)+RSA.Que(n-a+1,n-b+1)-1; // 向前和向后找LCP
                if(len<0) continue;
                if(len<i) continue;
                int l=a-RSA.Que(n-a+1,n-b+1)+1,r=b+SA.Que(a,b)-1;
                int p=SA.sa[ST.Que(l,r-(len/i+1)*i+1)];
                l=p,r=p+(len/i+1)*i-1;
                if(len/i+1>ans) ans=len/i+1,st=l,ed=r;
                else if(len/i+1==ans && (SA.rk[st]>SA.rk[l]||(SA.rk[st]==SA.rk[l]&& r-l+1<ed-st+1) ) ) st=l,ed=r;
            }
        }
        if(!ans) ans=1,st=SA.sa[1],ed=SA.sa[1];
        reverse(s+1,s+n+1);
        printf("Case %d: ",++kase);
        rep(i,st,ed) putchar(s[i]);
        putchar('\n');
    }
}

猜你喜欢

转载自www.cnblogs.com/chasedeath/p/12213819.html