zoukankan      html  css  js  c++  java
  • 后缀数组专题与代码模板

    后缀数组 DA(倍增)算法求 SA[N] 与 Rank[N] (时间O(NlogN),空间O(N))

    sa[i] : 表示 排在第i位的后缀 起始下标

    rank[i] : 表示后缀 suffix(i)排在第几

    height[i] : 表示 sa[i-1] 与 sa[i] 的LCP 值

    h[i]: 表示 suffix(i)与其排名前一位的 LCP值

    const int N = int(2e5)+10;
    int cmp(int *r,int a,int b,int l){
        return (r[a]==r[b]) && (r[a+l]==r[b+l]);
    }
    // 用于比较第一关键字与第二关键字,
    // 比较特殊的地方是,预处理的时候,r[n]=0(小于前面出现过的字符)
    
    int wa[N],wb[N],ws[N],wv[N];
    int rank[N],height[N];
    void DA(int *r,int *sa,int n,int m){ //此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i; //预处理长度为1
        for(j=1,p=1;p<n;j*=2,m=p) //通过已经求出的长度J的SA,来求2*J的SA
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i; // 特殊处理没有第二关键字的
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //利用长度J的,按第二关键字排序
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];  //基数排序部分
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  //更新名次数组x[],注意判定相同的
        }
    }
    
    void calheight(int *r,int *sa,int n){ // 此处N为实际长度
        int i,j,k=0;        // height[]的合法范围为 1-N, 其中0是结尾加入的字符
        for(i=1;i<=n;i++) rank[sa[i]]=i;  // 根据SA求RANK
        for(i=0;i<n; height[rank[i++]] = k ) // 定义:h[i] = height[ rank[i] ]
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++); //根据 h[i] >= h[i-1]-1 来优化计算height过程
    }
    
    char str[N];
    int sa[N];
    int main(){
        char str[N];
        scanf("%s",str);
        int n = strlen(str);
        str[n]=0;
        
        da(str,sa,n+1,128);  //注意区分此处为n+1,因为添加了一个结尾字符用于区别比较
        calheight(str,sa,n);
    }

    DC3 模板 (  时间复杂度O(N),空间复杂度O(3N) )

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int maxn = int(3e6)+10;
    const int N = maxn;
    
        #define F(x) ((x)/3+((x)%3==1?0:tb))
        #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
        int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
        int c0(int *r,int a,int b)
        {return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
        int c12(int k,int *r,int a,int b)
        {if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
        else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
        void sort(int *r,int *a,int *b,int n,int m)
        {
            int i;
            for(i=0;i<n;i++) wv[i]=r[a[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
            return;
        }
        void dc3(int *r,int *sa,int n,int m) //涵义与DA 相同
        {
            int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
            r[n]=r[n+1]=0;
            for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
            sort(r+2,wa,wb,tbc,m);
            sort(r+1,wb,wa,tbc,m);
            sort(r,wa,wb,tbc,m);
            for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
            rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
            if(p<tbc) dc3(rn,san,tbc,p);
            else for(i=0;i<tbc;i++) san[rn[i]]=i;
            for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
            if(n%3==1) wb[ta++]=n-1;
            sort(r,wb,wa,ta,m);
            for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
            for(i=0,j=0,p=0;i<ta && j<tbc;p++)
            sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
            for(;i<ta;p++) sa[p]=wa[i++];
            for(;j<tbc;p++) sa[p]=wb[j++];
            return;
        }

    训练题

      重复(出现)子串

        1.可重叠

          根据 height[i] = LCP{ suffix( sa[i-1] ), suffix( sa[i] ) } , 我们知道最长重复可重叠子串长度其实就是 Max{ height[i] }

       2.不可重叠

          poj 1743 Musical Theme

          重复出现的子串不能有重叠, 假定我们需要找一个长度为K,且不重叠的子串。 我们可以讲求出的height数组从1-n按其大于等于K进行分组,相同分组

    中 Max{ SAi } - Max{ SAj } > = k , 则存在满足要求的方案。

          对于本题,还需要预处理一些问题。 需要两个序列的差值相同,我们可以转换成前后的差值,然后将N个点的信息,收缩成N-1个段信息。然后就可以

    用模板做了。然后二分判定即可。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    const int N = int(2e5)+10;
    
    int cmp(int *r,int a,int b,int l){
        return (r[a]==r[b]) && (r[a+l]==r[b+l]);
    }
    int wa[N],wb[N],ws[N],wv[N];
    int rank[N],height[N];
    void DA(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            //printf("p = %d
    ", p );
        }
    }
    void calheight(int *r,int *sa,int n){
      //  memset(height,0,sizeof(height));
      //  memset(rank,0,sizeof(rank));
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);
    }
    
    int a[N], sa[N], n;
    
    vector<int> S[N];
    
    bool check(int k){
        bool flag = false;
        int cur = -1;
        for(int i = 1; i <= n; i++)
        {
            if( height[i] < k ) S[++cur].clear();
            S[cur].push_back(i);
        }
        for(int i = 0;i <= cur; i++)
        {
            int Max=-1,Min=N;
            if( S[i].size() > 1 ){
                for(int j = 0; j < S[i].size(); j++)
                {
                    Max = max( Max, sa[ S[i][j] ] );
                    Min = min( Min, sa[ S[i][j] ] );
                }
                if( Max-Min >= k ){flag = true; break;}
            }
        }
        return flag;
    }
    int solve(){
        DA(a,sa,n+1,200);
        calheight(a,sa,n);
        int l = 0, r = n, res = 0;
        while(l<=r)
        {
            int m = (l+r)>>1;
            if( check(m) ) res=m,l=m+1;
            else r = m-1;
        }
        return res>=4?res+1:0;
    }
    int main(){
        while( scanf("%d",&n), n )
        {
            for(int i = 0; i < n; i++)
                scanf("%d",&a[i]);
            for(int i = 0; i < n-1; i++)
                a[i] = a[i+1]-a[i]+90;
            a[--n] = 0;
            int ans = solve();
            printf("%d
    ", ans );
        }
        return 0;
    }
    View Code
           poj 3261 可重叠的K次最长重复子串
          我们做完后缀数组,得到height[]数组后,前缀相同的都相邻,且具有传递性。 
          因为 height[i] 表示 suffix( sa[i-1] ) 与 suffix( sa[i] )的 LCP, 我们统计出所有连续长度为K中的最小LCP,然后对所有最小LCP取最大值即为答案。这里可以用单调队列维护。
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<map>
    using namespace std;
    
    const int N = (int)2e5+10;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    int Q[N], sa[N];
    
    void solve(int *r,int n,int m,int K)
    {
        da(r,sa,n+1,m);
        calheight(r,sa,n);
        int ql = 0, qr = 0, Max = 0;
        for(int i = 2;i <= n; i++)
        {
            while( (qr>ql) && ( height[Q[qr-1]] >= height[i] ) ) qr--;
            Q[qr++] = i;
            if( i > K+1 ) while( (ql<qr) && (Q[ql]<=(i-K)) ) ql++;
            if( i >= K+1 )  Max = max( Max, height[Q[ql]] );
        }
    
        printf("%d
    ", Max);
    }
    
    int r[N], n, K;
    int tmp[N];
    map<int,int> mp;
    
    int main(){
        while( scanf("%d%d",&n,&K) != EOF)
        {
            for(int i = 0; i < n; i++)
            {
                scanf("%d",&r[i]);
                tmp[i] = r[i];
            }
            if( n == 1 && K == 1 ){ puts("1"); continue; }
            sort(tmp,tmp+n);
            int sz = unique(tmp,tmp+n)-tmp;
            mp.clear();
            for(int i = 0;i < sz; i++)
                mp[ tmp[i] ] = i+1;
            for(int i = 0; i < n; i++)
                r[i] = mp[ r[i] ];
            r[n] = 0;
            solve( r, n, sz+1, K-1 );
        }
        return 0;
    }
    View Code

      

      子串的个数

          spoj 694  不相同的子串个数,可以重叠。

          一个串S,其所有子串 Si, 必定是某个后缀 Suffix(k)的一个前缀。 我们依据此将问题转换成求所有后缀的,不相同前缀数量。

          将所有后缀按照  suffix( sa[1] ), suffix( sa[2] ), ..., suffix( sa[n] ) 依次放入然后计算。

          当一个串 S(有效范围 [1,n]), 则当一个后缀 suffix( sa[i] ) 加入进来, 其会产生 n-sa[i]+1 个前缀,并且会有height[i]个与前一个相同。

          所以加入当前后缀增加的不同前缀数量为:  n-sa[i]+1 - height[i].   然后整个串的不同子串数量即为,所有前缀产生的总和。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 1010;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    char str[N];
    int sa[N], n, r[N];
    
    void solve()
    {
        da(r,sa,n+1,128);
        calheight(r,sa,n);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += n-sa[i]-height[i];
        printf("%d
    ", ans );
    }
    int main(){
        int T;
        scanf("%d",&T);
        while( T-- )
        {
            scanf("%s", str);
            n = strlen(str);
            for(int i = 0; i < n; i++)
                r[i] = (int)str[i];
            r[n]=0;
            solve();
        }
        return 0;
    }
    View Code

           

          spoj 705 如上题一样,只是N扩大到了10^5, 上题N=1000,可以用DP来做

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 51010;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    char str[N];
    int sa[N], n, r[N];
    
    void solve()
    {
        da(r,sa,n+1,128);
        calheight(r,sa,n);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans += n-sa[i]-height[i];
        printf("%d
    ", ans );
    }
    int main(){
        int T;
        scanf("%d",&T);
        while( T-- )
        {
            scanf("%s", str);
            n = strlen(str);
            for(int i = 0; i < n; i++)
                r[i] = (int)str[i];
            r[n]=0;
            solve();
        }
        return 0;
    }
    View Code

      回文子串

          URAL 1297 将原串S,与倒序串S1,连接起来,连接出插入一特殊字符。

          分奇数和偶数枚举对称中心, 对于奇数 Lcp( rank[i], rank[ 2*n-i ] ). 对于偶数 Lcp( rank[i], rank[2*n+1-i] )

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int N = 2024;
    
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    char str[N];
    int r[N], sa[N], n;
    int dp[N][20];
    
    void InitRMQ(){
        int cnt = n+n+1;
        for(int i = 1; i <= cnt; i++)
            dp[i][0] = height[i];
        for(int i = 1; (1<<i) <= cnt; i++)
        {
            for(int j = 1; j+(1<<i)-1 <= cnt; j++)
                dp[j][i] = min( dp[j][i-1], dp[j+(1<<(i-1))][i-1] );
        }
    }
    int query(int L,int R){
        if(L > R) swap(L,R); L++;
        int k = (int)floor(  log(1.*(R-L+1)) / log(2.0) );
        int t = min( dp[L][k], dp[R-(1<<k)+1][k] );
        return t;
    }
    void solve(){
        int cnt = n+n+1;
        da(r,sa,cnt+1,128);
        calheight(r,sa,cnt);
    
        /*printf("n = %d
    ", n );
        for(int i = 1; i <= cnt; i++)
            printf("height[%d] = %d
    ", i, height[i]);
        for(int i = 0; i <= cnt; i++)
            printf("rank[%d] = %d, sa[%d] = %d
    ", i,rank[i],i,sa[i] );*/
        InitRMQ();
        int Max1 = 0, p1;
        // odd
        for(int i = 0; i < n; i++){
            int t = query( rank[i], rank[n+n-i] );
            if( t > Max1 ) Max1 = t, p1 = i;
        }
    
        int Max2 = 0, p2;
        // even
        for(int i = 1;i < n; i++){
            int t = query( rank[i], rank[n+n-i+1] );
            if( t > Max2 ) Max2 = t, p2 = i;
        }
    
    //    printf("Max1=%d,p1=%d, Max2=%d,p2=%d
    ",Max1,p1,Max2,p2);
        if( 2*Max1-1 >= 2*Max2 ){
            for(int j = p1-Max1+1; j <= p1+Max1-1; j++)
                printf("%c",str[j]); puts("");
        }
        else{
            for(int j = p2-Max2; j <= p2+Max2-1; j++)
                printf("%c", str[j]); puts("");
        }
    }
    int main(){
    
    //    printf("%lf
    ", log(5.0)/log(2.0) );
        while( scanf("%s",str) != EOF)
        {
            n = strlen(str);
            for(int i = 0; i < n; i++)
                r[i] = (int)str[i];
            r[n] = 1;
            int t = n;
            for(int i = n-1; i >= 0; i--)
                r[++t] = (int)str[i];
            r[2*n+1] = 0;
    
           // for(int i = 0; i <= 2*n; i++)
           //     printf("r[%d] = %d
    ", i, r[i] );
            solve();
        }
        return 0;
    }
    View Code

       连续重复子串

          poj 2406 虽然此题用KMP的nxt函数来做可能更合适点,不过我们还是用后缀数组来做一次,更熟悉后缀数组的作用。

          做法其实很简单,枚举长度L,若 a^k = S, 那么 显然 Lcp{ rank[0], rank[L] } = n-L.  因为LCP总是与rank[0]比,可以预处理下,这样空间复杂度就降下来了,不过N= 1e6,  DA的NlogN的预处理会TLE,需要用 DC3的O(N)才能勉强 2700ms 过。  KMP 100Ms就能跑过去。。。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N = int(1e6)+10;
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    void da(int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa,int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    int sa[N],r[N], n, dp[N];
    char str[N];
    
    void InitRMQ(){
        int x = rank[0]; dp[x] = n;
        for(int i = x-1; i >= 1; i--)
            dp[i] = min( dp[i+1], height[i+1] );
        for(int i = x+1; i <= n; i++)
            dp[i] = min( dp[i-1], height[i] );
    }
    vector<int> S;
    int main(){
        while( scanf("%s", str) != EOF )
        {
            if( strcmp(str,".") == 0 ) break;
            n = strlen(str);
            for(int i = 0; i < n; i++)
                r[i] = str[i];
            r[n] = 0;
    
            da(r,sa,n+1,128);
            calheight(r,sa,n);
    
            InitRMQ();
            S.clear();
            for(int i = 1; i*i <= n; i++)
            {
                if(n%i==0){
                    S.push_back(i);
                    if( i*i != n ) S.push_back(n/i);
                }
            }
            sort(S.begin(),S.end());
            for(vector<int>::iterator it = S.begin(); it != S.end(); it++ ){
                int L = *it;
                if( n%L == 0 ){
                    int t = dp[rank[L]]; //query( rank[0], rank[L] );
                    if( t == n-L ){
                         printf("%d
    ", n/L );
                         break;
                    }
                }
            }
        }
        return 0;
    }
    DA
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int maxn = int(3e6)+10;
    const int N = maxn;
    
        #define F(x) ((x)/3+((x)%3==1?0:tb))
        #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
        int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
        int c0(int *r,int a,int b)
        {return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
        int c12(int k,int *r,int a,int b)
        {if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
        else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
        void sort(int *r,int *a,int *b,int n,int m)
        {
            int i;
            for(i=0;i<n;i++) wv[i]=r[a[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
            return;
        }
        void dc3(int *r,int *sa,int n,int m)
        {
            int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
            r[n]=r[n+1]=0;
            for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
            sort(r+2,wa,wb,tbc,m);
            sort(r+1,wb,wa,tbc,m);
            sort(r,wa,wb,tbc,m);
            for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
            rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
            if(p<tbc) dc3(rn,san,tbc,p);
            else for(i=0;i<tbc;i++) san[rn[i]]=i;
            for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
            if(n%3==1) wb[ta++]=n-1;
            sort(r,wb,wa,ta,m);
            for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
            for(i=0,j=0,p=0;i<ta && j<tbc;p++)
            sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
            for(;i<ta;p++) sa[p]=wa[i++];
            for(;j<tbc;p++) sa[p]=wb[j++];
            return;
        }
    int height[N], rank[N];
    void calheight(int *r,int *sa,int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    int sa[N],r[N], n, dp[N];
    char str[N];
    
    void InitRMQ(){
        int x = rank[0]; dp[x] = n;
        for(int i = x-1; i >= 1; i--)
            dp[i] = min( dp[i+1], height[i+1] );
        for(int i = x+1; i <= n; i++)
            dp[i] = min( dp[i-1], height[i] );
    }
    vector<int> S;
    int main(){
        while( scanf("%s", str) != EOF )
        {
            if( strcmp(str,".") == 0 ) break;
            n = strlen(str);
            for(int i = 0; i < n; i++)
                r[i] = str[i];
            r[n] = 0;
    
            dc3(r,sa,n+1,128);
            calheight(r,sa,n);
    
            InitRMQ();
            S.clear();
            for(int i = 1; i*i <= n; i++)
            {
                if(n%i==0){
                    S.push_back(i);
                    if( i*i != n ) S.push_back(n/i);
                }
            }
            sort(S.begin(),S.end());
            for(vector<int>::iterator it = S.begin(); it != S.end(); it++ ){
                int L = *it;
                if( n%L == 0 ){
                    int t = dp[rank[L]]; //query( rank[0], rank[L] );
                    if( t == n-L ){
                         printf("%d
    ", n/L );
                         break;
                    }
                }
            }
        }
        return 0;
    }
    DC3

          poj 3693 连续重复次数最多的子串.

          枚举构成 子串的单元长度L,  那么将原串用长度L等分为(0,L,2L,...最后段可能不全).

            重复一次的我们特殊处理,显然是rank[1]所对应的单个字符。

          我们只考虑 “单元串” 重复两次或以上构成的子串,那么我们可以知道,若存在长度为L的“单元串”,则必定有一对相邻的点 { L*i,L*(i+1) } 分别属于两个不同的“单元串”, 当以这两个为起点的后缀的LCP为M,则其将已经匹配长度为L的“单元串“ M/L+1次。  这里有个问题是,我们求得的 单元串重复多次构成不一定是最优或者起点,因为 M%L != 0的时候,可能不是起点, 这个时候  起点可能是  L*i - (L-M%L) , 抽象理解是补全循环,使其成为L的倍数,让它重复次数最大。

          虽然这样,我们可以求出 最大重复次数 与其对应的长度。 但是题目需要求最小的字典序。 我们可以将最大重复次数与 合法的长度都存储起来(可能有多个合法长度,但重复次数唯一),然后我们可以 按排名从1-N分别求出对应长度的 最小字典序,然后取一个最小的排名即可。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N = int(1e5)+10;
    int cmp(int *r,int a,int b,int l){
        return (r[a]==r[b]) && (r[a+l]==r[b+l]);
    }
    // 用于比较第一关键字与第二关键字,
    // 比较特殊的地方是,预处理的时候,r[n]=0(小于前面出现过的字符)
    
    int wa[N],wb[N],ws[N],wv[N];
    int rank[N],height[N];
    void da(int *r,int *sa,int n,int m){ //此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i; //预处理长度为1
        for(j=1,p=1;p<n;j*=2,m=p) //通过已经求出的长度J的SA,来求2*J的SA
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i; // 特殊处理没有第二关键字的
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //利用长度J的,按第二关键字排序
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];  //基数排序部分
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  //更新名次数组x[],注意判定相同的
        }
    }
    
    void calheight(int *r,int *sa,int n){ // 此处N为实际长度
        int i,j,k=0;        // height[]的合法范围为 1-N, 其中0是结尾加入的字符
        for(i=1;i<=n;i++) rank[sa[i]]=i;  // 根据SA求RANK
        for(i=0;i<n; height[rank[i++]] = k ) // 定义:h[i] = height[ rank[i] ]
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++); //根据 h[i] >= h[i-1]-1 来优化计算height过程
    }
    int sa[N],r[N], n;
    char str[N];
    int dp[N][20];
    
    void InitRMQ(){
        for(int i = 1; i <= n; i++)
            dp[i][0] = height[i];
        for(int j = 1; (1<<j)<=n; j++)
            for(int i = 1; i+(1<<j)-1<=n; i++)
                dp[i][j] = min( dp[i][j-1], dp[i+(1<<(j-1))][j-1] );
    }
    int query(int L,int R){
        if(L>R) swap(L,R); L++;
        int k = (int)floor( log(1.*(R-L+1))/log(2.0) );
        return min( dp[L][k], dp[R-(1<<k)+1][k] );
    }
    int main(){
        int Case = 1;
        while( scanf("%s",str) != EOF)
        {
            if(strcmp(str,"#")==0) break;
             printf("Case %d: ", Case++ );
    
            n = strlen(str);
            for(int i = 0; i < n; i++)
                r[i] = str[i]-'a'+1;
            r[n] = 0;
    
            da(r,sa,n+1,27);
            calheight(r,sa,n);
            InitRMQ();
            int maxk = 0, maxl = 0;
            vector<int> S; S.clear();
            for(int L = 1; L < n; L++)
            {
    //            printf("L = %d
    ", L);
                for(int i = 0; i+L < n; i+=L )
                {
                    int M = query( rank[i], rank[i+L] );
                    int k = M/L+1, left = i;
                    int t = L-M%L;
                    t = i-t;
                  //  printf("t = %d, M = %d, i = %d
    ", t, M, i );
                    if( (t>=0) && (M%L) )
                    {
                        int M1 = query( rank[t], rank[t+L] );
                        int k1 = M1/L+1;
                        if( k1 > k ) k = k1;
                    }
    //                printf("i = %d, k = %d, L = %d
    ", i, k, L);
                    if( k > maxk ){
                        maxk = k; maxl = L;
                        S.clear(); S.push_back(L);
                    }
                    else if( (k==maxk) && (maxl<L) )
                        S.push_back(L), maxl = L;
                }
            }
            // debug
    //        for(int i = 0; i < S.size(); i++)
    //            printf("L = %d
    ", S[i] );
    
            if( (S.size()==0) || (maxk==1) ){
                printf("%c
    ", str[sa[1]] );
                continue;
            }
            int st, len = -1;
            vector< pair<int,int> >res; res.clear();
    
    //        printf("maxk = %d
    ", maxk );
            for(int j = 0; j < (int)S.size(); j++)
            {
                int L = S[j];
    //            printf("===> L = %d
    ", L);
                for(int i = 1; i <= n; i++)
                {
                    if( sa[i]+L < n ){
                        int M = query( i, rank[sa[i]+L] );
    //                    printf("i=%d,[%d,%d], M = %d
    ", i,sa[i],sa[i]+L, M );
                        if( M >= L*(maxk-1) ){
                            res.push_back( make_pair(sa[i],L) );
                            break;
                        }
                    }
                }
            }
    //        printf("Debug:
    ");
    //        printf("LCP(3,5) = %d
    ", query( rank[3], rank[5] ));
    //         for(vector< pair<int,int> >::iterator it=res.begin(); it != res.end(); it++ )
    //         {
    //             printf("st = %d, L = %d, rank = %d
    ", it->first, it->second, rank[it->first]);
    //         }
            int Min = n+1;
            for(vector< pair<int,int> >::iterator it=res.begin(); it != res.end(); it++ )
            {
                int a = it->first, b = it->second;
                if( rank[a] < Min ) st = a, len = b, Min = rank[a];
            }
            len *= maxk;
            //printf("st = %d
    ", st );
            for(int i = 0; i < len; i++)
                printf("%c", str[st+i] );
            puts("");
        }
        return 0;
    }
    View Code

        spoj 687 repeats  做法同上题样,只要求最大次数,代码简单了多。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N = int(1e5)+10;
    int cmp(int *r,int a,int b,int l){
        return (r[a]==r[b]) && (r[a+l]==r[b+l]);
    }
    // 用于比较第一关键字与第二关键字,
    // 比较特殊的地方是,预处理的时候,r[n]=0(小于前面出现过的字符)
    
    int wa[N],wb[N],ws[N],wv[N];
    int rank[N],height[N];
    void da(int *r,int *sa,int n,int m){ //此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界
        int i,j,p,*x=wa,*y=wb,*t;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i; //预处理长度为1
        for(j=1,p=1;p<n;j*=2,m=p) //通过已经求出的长度J的SA,来求2*J的SA
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i; // 特殊处理没有第二关键字的
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //利用长度J的,按第二关键字排序
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];  //基数排序部分
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  //更新名次数组x[],注意判定相同的
        }
    }
    
    void calheight(int *r,int *sa,int n){ // 此处N为实际长度
        int i,j,k=0;        // height[]的合法范围为 1-N, 其中0是结尾加入的字符
        for(i=1;i<=n;i++) rank[sa[i]]=i;  // 根据SA求RANK
        for(i=0;i<n; height[rank[i++]] = k ) // 定义:h[i] = height[ rank[i] ]
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++); //根据 h[i] >= h[i-1]-1 来优化计算height过程
    }
    int sa[N],r[N], n;
    char str[N];
    int dp[N][20];
    
    void InitRMQ(){
        for(int i = 1; i <= n; i++)
            dp[i][0] = height[i];
        for(int j = 1; (1<<j)<=n; j++)
            for(int i = 1; i+(1<<j)-1<=n; i++)
                dp[i][j] = min( dp[i][j-1], dp[i+(1<<(j-1))][j-1] );
    }
    int query(int L,int R){
        if(L>R) swap(L,R); L++;
        int k = (int)floor( log(1.*(R-L+1))/log(2.0) );
        return min( dp[L][k], dp[R-(1<<k)+1][k] );
    }
    int main(){
        int T;
        scanf("%d", &T);
        while(T--){
            scanf("%d", &n);
            for(int i = 0; i < n; i++){
                scanf("%s", str );
                r[i] = str[0]-'a'+1;
            }
            r[n] = 0;
    
            da(r,sa,n+1,27);
            calheight(r,sa,n);
            InitRMQ();
            int maxk = 0, maxl = 0;
            for(int L = 1; L < n; L++)
            {
    //            printf("L = %d
    ", L);
                for(int i = 0; i+L < n; i+=L )
                {
                    int M = query( rank[i], rank[i+L] );
                    int k = M/L+1, left = i;
                    int t = L-M%L;
                    t = i-t;
                  //  printf("t = %d, M = %d, i = %d
    ", t, M, i );
                    if( (t>=0) && (M%L) )
                    {
                        int M1 = query( rank[t], rank[t+L] );
                        int k1 = M1/L+1;
                        if( k1 > k ) k = k1;
                    }
    //                printf("i = %d, k = %d, L = %d
    ", i, k, L);
                    if( k > maxk ) maxk = k;
                }
            }
            printf("%d
    ", maxk);
        }
        return 0;
    }
    View Code

      两个字符串相关问题

         两个串的最长公共子串长度

          poj 3693  将两串拼接起来,中间连接出插入特殊字符, 然后问题就转换成了求最长的LCP,其满足两个后缀分属不同的串, 简单推理下,可以知道,根据传递性,两串的最长LCP必定相邻, 可以用反证法证明.

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    const int N = int(2e5)+10;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    
    char s1[N], s2[N];
    int r[N], n1, n2, n;
    int sa[N];
    
    int main(){
        while( scanf("%s%s",s1,s2) != EOF)
        {
            n1 = strlen(s1); n2 = strlen(s2);
            // {0,n1-1}, n1+{ 1,n2 }
            for(int i = 0; i < n1; i++)
                r[i] = (int)s1[i];
            r[n1] = 1;
            for(int i = 0; i < n2; i++)
                r[ n1+1+i ] = (int)s2[i];
            r[n1+n2+1] = 0;
            n = n1+n2;
    
            da(r,sa,n+1,128);
            calheight(r,sa,n);
            int Max = 0, st;
            for(int i = 2; i <= n; i++)
            {
                if( height[i] > Max ){
                    if( (sa[i-1]<n1&&sa[i]>n1) || (sa[i-1]>n1&&sa[i]<n1) )
                        Max = height[i], st = sa[i]>n1?sa[i-1]:sa[i];
                }
            }
            printf("%d
    ", Max);
           /* for(int i = 0; i < Max; i++)
                printf("%c", s1[st+i]);
            puts("");*/
        }
        return 0;
    }
    View Code

      

          spoj 687 同上题一样,不过注意 N需要开到 3e5.否则会RE

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    const int N = int(3e5)+100;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    
    char s1[N], s2[N];
    int r[N], n1, n2, n;
    int sa[N];
    
    int main(){
        while( scanf("%d", &n) != EOF)
       // while( scanf("%s%s",s1,s2) != EOF)
        {
            scanf("%s",s1); scanf("%s",s2);
            n1 = strlen(s1); n2 = strlen(s2);
            // {0,n1-1}, n1+{ 1,n2 }
            for(int i = 0; i < n1; i++)
                r[i] = (int)s1[i];
            r[n1] = 1;
            for(int i = 0; i < n2; i++)
                r[ n1+1+i ] = (int)s2[i];
            r[n1+n2+1] = 0;
            n = n1+n2;
    
            da(r,sa,n+1,128);
            calheight(r,sa,n);
            int Max = 0, st;
            for(int i = 2; i <= n; i++)
            {
                if( height[i] > Max ){
                    if( (sa[i-1]<n1&&sa[i]>n1) || (sa[i-1]>n1&&sa[i]<n1) )
                        Max = height[i], st = (sa[i]<n1?sa[i]:sa[i-1]);
                }
            }
          //  printf("%d
    ", Max);
            for(int i = 0; i < Max; i++)
                printf("%c", s1[st+i]);
            puts("");
        }
        return 0;
    }
    View Code

         两个串的公共子串个数

          poj 3415 求 长度不小于K的公共子串个数(可相同)。题目比较经典~~

          分析过程:首先主观想法是,串A长度为n1,串B长度为n2, 总数量就是两个串的笛卡尔积。串A的n1个后缀与串B的n2个后缀满足要求的匹配数量。

          对于10^5的N,O(N^2)显然不行,我们可以尝试着转换,是否计算过程能否转换成O(N)或者O(nlogn)的。

          再仔细分析下,计算的过程,枚举串A所有后缀 st, 与B的所有后缀匹配。 我们提出一个猜想,当我们枚举串A的一个后缀suffix(i)与串B所有后缀计算完成后,再枚举 串A的后缀suffix(i+1) ,能否利用suffix(i)的计算信息来降低 扫描串B的所有后缀的 时间花费呢。 显然suffix(i) 与 suffix(i+1) 之间的关系对于公共子串而言 并不关键。但是LCP(最长公共前缀)就比较有用了。

             关于排序后的后缀的性质这里就不说了。我们直接切入主题,相邻后缀不行,我们尝试使用相邻排名来处理。若我们将两个串连接起来,(连接点插入一个特殊字符)。

           枚举串A的所有排名等价枚举所有A的后缀.当我们按照排名从小到大枚举 后缀x(可能是A的,也有可能是B的), 若假定此时是A的一个后缀, 那其前面的(排名比起小的)后缀中B的一个后缀y, 若与A的后缀x之间的 LCP( x,y ) >= k,  则此时有 ,若 y < x , 公共子串数量为y-k+1,  若 y > x, 公共子串数量为x-k+1,  显然与期间的最小值有关。

          显然,若这样计算,我们只计算了 A的所有后缀x 与 排名比其小的B的后缀, 那些比X大的B的后缀还未被计算到。 下意识会想到倒着做一次,就可以了。 其实我们可以对B同A那样求一次,就等价反向再对A求一次了。

          大概计算的思路就在这里了, 不过我们还需要解决如何利用前面的值来简化求解时间。

          为了便于分析,我们假定只考虑 计算后缀排名在A之前的这一部分,因为另一部分是类似的。

          首先我们将 height[i] = max( 0, height[i]-K+1 ),表示当前后缀贡献的子串数量。

          维护一个 严格单调递增的 栈,在处理过程中,我们需要记录一个当前合法的串B的后缀贡献的子串数量,当加入进来一个A串时候,所有比起小的串B后缀都应该被纳入计算,比起大的部分我们应该减掉,  此时就需要记录那部分的数量。 中间过程不太好用文字描述。。。具体看代码吧。

           

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = int(2e5)+10;
    typedef long long LL;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    int n, n1, n2, K;
    int sa[N], r[N];
    char s1[N], s2[N];
    
    int h[N],na[N],nb[N],S[N];
    
    void solve(){
        for(int i = 2; i <= n; i++)
            h[i] = max(0,height[i]-K+1);
        LL ans = 0, w1 = 0, w2 = 0;
        int top = 0;
        for(int i = 2; i <= n; i++)
        {
            S[++top] = h[i];
            if( sa[i-1] <= n1 ) na[top]=1,nb[top]=0,w1 += h[i];
            else na[top]=0, nb[top]=1, w2 += h[i];
            while( (top>1) && (S[top]<=S[top-1]) ){
                w1 -= na[top-1]*(S[top-1]-S[top]);
                w2 -= nb[top-1]*(S[top-1]-S[top]);
                na[top-1] += na[top]; nb[top-1] += nb[top];
                S[top-1] = S[top];
                top--;
            }
            if( sa[i] <= n1 ) ans += w2;
            else ans += w1;
        }
        printf("%lld
    ", ans);
    }
    int main(){
        while( scanf("%d", &K), K)
        {
            scanf("%s%s",s1,s2);
            n1 = strlen(s1); n2 = strlen(s2);
            for(int i =  0; i < n1; i++)
                r[i] = (int)s1[i];
            r[n1] = 1;
            for(int i = 0; i < n2; i++)
                r[n1+1+i] = (int)s2[i];
            r[n=n1+n2+1] = 0;
    
            da(r,sa,n+1,128);
            calheight(r,sa,n);
            solve();
        }
        return 0;
    }
    View Code

      多个字符串

          通常解法是将其连接成一个串,然后跑DC3 or DA。 

          poj 3294  解法是二分枚举长度公共串长度L,将height值按>=L分组。然后随便搞就好了。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N = int(3e5)+10;
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    char str[110][1010];
    int r[N], sa[N], n, len[110], a[110], cnt;
    bool vis[N];
    int K;
    
    vector<int> S[N];
    bool check(int L){
        int cur = -1;
        for(int i = 1; i <= n; i++){
            if( height[i] < L ) S[++cur].clear();
            S[cur].push_back(i);
        }
        for(int i = 0; i <= cur; i++){
            if( S[i].size() > K ){
                memset(vis,0,sizeof(vis));
                for(int j = 0; j < S[i].size(); j++)
                {
                    int k = S[i][j];
                    int x = upper_bound(a,a+cnt+1, sa[ S[i][j] ] )-a-1;
                    vis[x] = true;
                }
                int count = 0;
                for(int j = 0; j < cnt; j++)
                    if(vis[j]) count++;
                if( count > K ) return true;
            }
        }
        return false;
    }
    void gao(int L){
        int cur = -1;
        for(int i = 1; i <= n; i++){
            if( height[i] < L ) S[++cur].clear();
            S[cur].push_back(i);
        }
        for(int i = 0; i <= cur; i++){
            if( S[i].size() > K ){
                memset(vis,0,sizeof(vis));
                for(int j = 0; j < S[i].size(); j++)
                {
                    int k = S[i][j];
                    int x = upper_bound(a,a+cnt+1, sa[ S[i][j] ] )-a-1;
                    vis[x] = true;
                }
                int count = 0;
                for(int j = 0; j < cnt; j++)
                    if(vis[j]) count++;
                if( count > K )
                {
                    for(int j = 0; j < L; j++ )
                        printf("%c", char(r[ sa[S[i][0]]+j ]) );
                    puts("");
                }
            }
        }
    }
    void solve(){
        da(r,sa,n+1,310);
        calheight(r,sa,n);
        K = cnt/2;
    
        int l = 1, r = 1000, L = 0;
        while( l <= r ){
            int m = (l+r)>>1;
            if( check(m) ) L=m,l=m+1;
            else r = m-1;
        }
        if( L == 0 ) puts("?");
        else gao(L);
    }
    int main(){
        int Case = 0;
        while( scanf("%d",&cnt), cnt )
        {
            if( Case++ > 0 ) puts("");
            for(int i = 0; i < cnt; i++)
            {
                scanf("%s",str[i]);
                len[i] = strlen(str[i]);
            }
            n = 0;
            int tmp = 200;
            for(int i = 0; i < cnt; i++)
            {
                if(i>0) r[n++] = tmp++;
                for(int j = 0; j < len[i]; j++)
                    r[n++] = (int)str[i][j];
            }
            r[n] = 0;
    
            tmp = 0;
            for(int i = 0; i <= cnt; i++)
            {
                a[i] = tmp;
                if( i < cnt ) tmp = tmp + (i==0?len[i]:len[i]+1);
            }
            solve();
        }
        return 0;
    }
    View Code

          spoj 220 题目给出10个长度为10^4的串,让求在每个串中都至少出现两次的不重叠 子串最大长度。

          做法同样是合并成一个串,然后二分子串长度L,因为不同串数量才10,暴力做判定就好了。关于不重叠和前面一样同一组Max-Min>=L即可。不过这里要注意是对于每一种串都必须满足才可。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N = int(3e5)+10;
    
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b] && r[a+l]==r[b+l];
    }
    int wa[N],wb[N],ws[N],wv[N];
    int height[N], rank[N];
    
    void da(int *r,int *sa,int n,int m){
        int i,j,p,*x=wa,*y=wb;
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[x[i]=r[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
        for(j=1,p=1;p<n;j*=2,m=p)
        {
            for(p=0,i=n-j;i<n;i++) y[p++]=i;
            for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0;i<m;i++) ws[i]=0;
            for(i=0;i<n;i++) wv[i]=x[y[i]];
            for(i=0;i<n;i++) ws[wv[i]]++;
            for(i=1;i<m;i++) ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
    }
    void calheight(int *r,int *sa, int n){
        int i,j,k=0;
        for(i=1;i<=n;i++) rank[sa[i]]=i;
        for(i=0;i<n; height[rank[i++]] = k )
            for(k?k--:0,j=sa[rank[i]-1]; r[j+k]==r[i+k];k++);
    }
    
    
    
    char str[15][10100];
    int len[15], a[N], n, M;
    int sa[N], r[N];
    vector<int> S[N];
    int Min[15], Max[15];
    
    bool check(int L){
        int cur = -1;
        for(int i = 1; i <= n; i++){
            if( height[i] < L ) S[++cur].clear();
            S[cur].push_back(i);
        }
        for(int i = 0; i <= cur; i++)
        {
            if( S[i].size() >= 2*M ){
                memset(Min,-1,sizeof(Min));
                memset(Max,-1,sizeof(Max));
    
                for(int j = 0; j < S[i].size(); j++)
                {
                    int v = S[i][j]; // rank
                    int idx = upper_bound(a,a+M+1,sa[v])-a-1;
                    Min[idx] = Min[idx]==-1? sa[v]: min(Min[idx],sa[v]);
                    Max[idx] = Max[idx]==-1? sa[v]: max(Max[idx],sa[v]);
                }
                bool flag = true;
                for(int i = 0; i < M; i++){
                    if( (Min[i]==-1) || (Max[i]-Min[i]<L) )
                    {
                        flag = false; break;
                    }
                }
                if(flag) return true;
            }
        }
        return false;
    }
    void solve(){
        da(r,sa,n+1,128);
        calheight(r,sa,n);
        int l = 1, r = N, ans = 0;
        while( l <= r ){
            int m = (l+r)>>1; //printf("m = %d
    ", m);
            if( check(m) ) ans=m, l = m+1;
            else r = m-1;
        }
        printf("%d
    ", ans );
    }
    int main(){
        int _;
        scanf("%d", &_);
        while( _-- )
        {
            scanf("%d", &M);
            for(int i = 0; i < M; i++)
            {
                scanf("%s", str[i]);
                len[i] = strlen(str[i]);
            }
            n = 0; int tmp = 0;
            for(int i = 0; i < M; i++)
            {
                if(i > 0) r[n++] = ++tmp;
                for(int j = 0; j < len[i]; j++)
                    r[n++] = int(str[i][j]);
            }
            r[n] = 0; tmp = 0;
            for(int i = 0; i <= M; i++){
                a[i] = tmp;
                if(i < M) tmp += (i==0)?len[i]:len[i]+1;
            }
            solve();
        }
        return 0;
    }
    View Code
  • 相关阅读:
    详解Codis安装与部署
    停车场地图开发
    dlib换脸
    海康相机官网硬触发设置
    python操作数据库
    心率和血氧测量
    音乐模块
    flask网页显示图片
    树莓派识别二维码
    树莓派python获取自身IP
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3233611.html
Copyright © 2011-2022 走看看