后缀数组学习

两份必备资料:

罗穗骞《后缀数组——处理字符串的有力工具》

斌牛博客对上面这篇论文的详细讲解。


论文给出的强大模板:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;
const int maxn=100000 + 50 ;

// "后缀i" : 以i开头的后缀。

char s[maxn];
int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; // 辅助数组
int r[maxn]; // 原字符串
int sa[maxn]; // 后缀数组 ,即按字典序排在第i位的是哪个后缀? (排序均为从小到大)
int rank[maxn];// 名次数组, 即以i开头的后缀排第几? (从小到大)
int height[maxn]; // 排第i位的后缀和排第i-1位的后缀(即sa[i]和sa[i-1])的最长公共前缀长度。

int cmp(int *r,int a,int b,int l)
{ /* 由于在原串末尾加了个0 ,避免了数组越界,换句话说:若a+l > max_len 或
    b+l > max_len, 则必有r[a]!=r[b] ; 如果现在看不懂没关系,读完整个程序再理解!
    */
    return r[a]==r[b]&&r[a+l]==r[b+l];
}

void da(int *r,int *sa,int n,int m) // 难点和重点!  计算后缀数组。
{              //   n : 字符串长度 , m: 字符ASCII码(即为关键字)的最大值 + 1.
    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]]++; // x[] 保存的是关键字, 由于只用来比较大小, 不必要连续。
    for(i=1; i<m; i++) ws[i]+=ws[i-1];
    for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i; // sa[] 就是只对长度为1的关键字排序得到的后缀数组!!
    // 为什么要从n-1到0枚举?  看后面的说明。
    /* 上面4行被称为基数排序,不懂没关系,手算模拟下就能理解, 如:
    ① x[] = r[] = {1,2,3,2,1,0}   // 末尾一定加了个0
    ② ws[0]=1 , ws[1]=2, ws[2]=2, ws[3]= 1 ; // 每个关键字的数量
    ③ ws[0]=1 , ws[1]=3, ws[2]=5, ws[3]= 6 ; // 关键字个数前缀和,表明每个关键字应该拍的位置。】
    ④ sa[0]=5 , sa[1]=0, sa[2]=4, sa[3]=1, sa[4]=3, sa[5]=4 ; // 看!已经排好序了!
    */

    // 下面就是倍增过程。j: 当前长度。 p:不同关键字的个数。
    for(j=1,p=1; p<n; j*=2,m=p)
    {
        /*
        下面2行按第二关键字基数排序排序,得到的后缀数组存在y[]中。
        根据上次的sa[] , 可直接计算。
        */
        for(p=0,i=n-j; i<n; i++) y[p++]=i; // 最后面的j个"第二关键为0的后缀"排名肯定是最小的!
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;// 减去j得到整体的开头位置.

        /*
        下面5行按第一关键字进行基数排序排序,得到的后缀数组存在sa[]中。
        */
        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];
        /*
            现在来说明为什么要从n-1枚举到0, 这样做的好处是:
            第一关键字相同的两个元素还会保持原来的顺序,即按第二关键字排序的结果。
            这就是所谓的稳定排序!
        */

        /*
        下面2行从新计算每个后缀的名次, 保存在x[]中
        */
        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++;
    }
    return;
}

/*
    下面的函数用来线性计算rank[]和height[].
    原理是:  height[rank[i]]  >=  height[rank[i-1]] - 1 .
    其证明请参看相关资料。
*/
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[i+k]==r[j+k]; k++);
    return;
}

// 调用方法:
int main()
{
    scanf("%s",s) ;
    int n= strlen(s) , m =0;
    for(int i=0; i<n; i++) r[i] = s[i] , m = max(m , r[i]) ;
    r[n++] = 0; //  注意末尾添加0

    da(r ,sa ,n , m+1);
    calheight(r,sa,n);

    /*
     然后利用sa[] ,rank[] 和height[] 解决问题。
    */

    return 0;
}



常用处理技巧:

① 分组(最常用):把排序后的后缀分成若干组,其中每组的后缀之间的 height 值都不小于 k。

②不可重叠: 只须判断每组的每个后缀的 sa 值的最大值和最小值之差是否不小于k。

③重复至少K次:判断的是有没有一个组的后缀个数不小于 k。

④不同子串个数:suffix(sa[k])将“贡献”出 n-sa[k]+1- height[k]个不同的子串,累加求和。

⑤最长回文子串:将整个字符串反过来写在原字符串后面。

⑥重复次数最多的连续重复子串:先穷举长度 L,S 肯定包括了字符 r[0], r[L], r[L*2],r[L*3], ……中的某相邻的两个。只须看字符 r[L*i]和 r[L*(i+1)]往前和往后各能匹配到多远,记这个总长度为 K,那么这里连续出现了 K/L+1 次。

⑦最长公共子串:求排名相邻但原来不在同一个字符串中的两个后缀的 height 值的最大值。

⑧长度不小于 k 的公共子串的个数: 扫描一遍,每遇到一个 B 的后缀就统计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,这里 A 的后缀需要用一个单调的栈来高效的维护。 这个技巧较高。

⑨可“整体偏移,变调”: 处理区差分串。



练习:

1. POJ 1226 Substrings (论文2.4 多个字符串,出现或反转后出现在每个字符串中的最长子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
typedef long long LL;
const int maxn=21111 ;

char ss[maxn];
int r[maxn],sa[maxn],rank[maxn],height[maxn],id[maxn] ;
int wa[maxn],wb[maxn],ky[maxn],cn[maxn] ;

inline bool cmp(int*r,int a,int b,int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(int *r,int *sa,int n , int m){
    int i,k,p,*x=wa,*y=wb;
    for(i=0; i<m; i++) cn[i] = 0;
    for(i=0; i<n; i++) cn[ x[i]=r[i] ]++ ;
    for(i=1; i<m; i++) cn[i] += cn[i-1] ;
    for(i=n-1; i>=0 ;i--) sa[ --cn[x[i]] ] = i;
    for(k=1,p=1; p<n ; k<<=1 ,m=p){
        for(i=n-k, p=0; i<n ;i++) y[p++] = i ;
        for(i=0; i<n; i++) if(sa[i]>=k) y[p++]= sa[i]-k ;
        for(i=0; i<n; i++) ky[i] = x[y[i]] ;
        for(i=0; i<m; i++) cn[i] = 0;
        for(i=0; i<n; i++) cn[ky[i]]++ ;
        for(i=1; i<m; i++) cn[i] += cn[i-1] ;
        for(i=n-1; i>=0 ;i--)  sa[--cn[ky[i]]] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,p=1,i=1;i<n;i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
    return ;
}
void get_height(int *r ,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i ;
    int k=0;
    for(int i=0;i<n;i++){
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k;
    }
}

bool check(int n, int N, int limit){
    set<int>vis;
    vis.insert(id[sa[1]]) ;
    for(int i=2;i<n;i++) {
        while(i<n && height[i] >= limit) vis.insert(id[sa[i++]]) ;
        if((int)vis.size() >=N) return true ;
        vis.clear() ;
        vis.insert(id[sa[i]]) ;
    }
    return false ;
}

int main()
{
    int  T;
    scanf("%d",&T);
    while(T--){
        int N, n=0, mx = 'z'+1 ;
        scanf("%d" ,&N);
        for(int i=1,j=0; i<=N; i++) {
            scanf("%s",ss);
            for(j=0;ss[j];j++) id[n]=i , r[n++]=ss[j];
            id[n]=i, r[n++] = mx++ ;
            for(j--;j>=0;j--) id[n]=i , r[n++] = ss[j] ;
            id[n]=i , r[n++] = mx++ ;
        }
        id[n] = -1 , r[n++] = 0 ;
        if(N==1){
            printf("%d\n" , strlen(ss)) ;
            continue ;
        }
        build_sa(r,sa,n,mx);
        get_height(r,n);
        int L=0 , R = 100 , mid ;
        while(L<R){
            mid = L + (R-L+1)/2;
            if(check(n,N,mid)) L = mid ;
            else R = mid-1 ;
        }
        printf("%d\n" , L);
    }
    return 0;
}

2. UVA - 11107 Life Forms  (论文2.4 多个字符串,不小于 k 个字符串中的最长子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
const int maxn = 110 * 1011 ;

char read[maxn] ;
int r[maxn] ;
int sa[maxn], height[maxn] ,rank[maxn] , id[maxn] ;
int wa[maxn] ,wb[maxn], ky[maxn], cn[maxn] ;
inline bool cmp(int *r ,int a,int b,int l){
    return r[a]==r[b] && r[a+l] == r[b+l] ;
}
void build_sa(int *r, int *sa ,int n , int m){
    int i, k , p , *x=wa ,*y = wb;
    for(i=0; i<m; i++) cn[i] = 0;
    for(i=0; i<n; i++) cn[ x[i] = r[i] ] ++ ;
    for(i=1; i<m; i++) cn[i] += cn[i-1] ;
    for(i=n-1; i>=0; i--) sa[ --cn[ x[i] ] ] = i ;
    for(k=1,p=1; p<n; k<<=1 , m=p) {
        for(p=0,i=n-k; i<n; i++) y[p++] = i ;
        for(i=0; i<n; i++) if(sa[i] >=k ) y[p++] = sa[i] - k ;
        for(i=0; i<n; i++) ky[i] = x[ y[i] ] ;
        for(i=0; i<m; i++) cn[i] = 0;
        for(i=0; i<n; i++) cn[ky[i]]++ ;
        for(i=1; i<m; i++) cn[i] += cn[i-1] ;
        for(i=n-1; i>=0; i--) sa[ --cn[ky[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],k) ? p-1 : p++ ;
    }
    return ;
}
void get_height(int *s, int n) {
    int i, j , k=0;
    for(i=0; i<n; i++) rank[sa[i]] = i ;
    for(i=0; i<n-1; i++) {
        if(k) k-- ;
        j = sa[rank[i]-1] ;
        while(s[i+k] == s[j+k]) k++ ;
        height[rank[i]] = k;
    }
}

bool check(int n ,int m, int mid) {
    set<int>vis ;
    vis.insert( id[ sa[1] ] ) ;
    for(int i=2; i<n; i++) {
        while(i<n && height[i] >= mid) vis.insert(id[ sa[i] ]) , i++ ;
        if(vis.size()*2 > m) return true ;
        vis.clear();
        vis.insert(id[ sa[i] ]) ;
    }
    return false ;
}
void print(int n, int m, int p) {
    set<int>vis ;
    vis.insert(id[ sa[1] ]) ;
    for(int i=2; i<n; i++) {
        while(i<n && height[i] >= p) vis.insert(id[sa[i]]) , i++ ;
        if(vis.size() * 2 > m) {
            int a = sa[i-1] ;
            for(int j=0; j<p ;j++) putchar(r[a+j]) ;
            puts("") ;
        }
        vis.clear() ;
        vis.insert(id[sa[i]]) ;
    }
}

int main()
{ 
    int m , first = 1 ;
    while(scanf("%d" ,&m)==1 && m) {
        if(!first) puts("") ; first = 0;
        int n = 0;
        for(int i=1; i<=m; i++) {
            scanf("%s" , read) ;
            for(int j=0; read[j] ; j++) {
                id[n]=i;
                r[n++] = read[j] ;
            }
            id[n] = i;
            r[n++] = 'z' + i ;
        }
        if( m==1 ){
            puts(read) ;
            continue ;
        }
        id[n] = m+1 , r[n++] = 0 ;
        build_sa(r , sa , n, 'z'+m+2) ;
        get_height(r , n) ;

        // debug :
       // for(int i=1; i<n ; i++)
         //   printf("%d  %d : %s\n", i, height[i] , r+sa[i]);

        if(!check(n , m , 1)) {
            puts("?") ;
            continue ;
        }
        int L = 1 , R = 1000;
        while(L<R) {
            int mid = L + (R-L+1)/2 ;
            if(check(n , m, mid)) L = mid ;
            else R = mid -1 ;
        }
        print(n ,m ,L) ;
    }
    return 0;
}

3.  POJ 1743 Musical Theme (论文2.2.1, 单个字符串, 不可重叠最长重复子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
const int INF = 1e8;
const int maxn = 20100;

int r[maxn] , s[maxn] , head[maxn] ;
int wa[maxn] , wb[maxn] , ky[maxn] , cn[maxn] ;
int sa[maxn] , height[maxn] , rank[maxn] ;

inline bool cmp(int *r,int a,int b,int l) {
    return  r[a]==r[b] && r[a+l]==r[b+l];
}
void build_sa(int *r ,int *sa ,int n ,int m) {
    int i ,k ,p ,*x=wa ,*y=wb ;
    for(i=0; i<m; i++) cn[i] = 0;
    for(i=0; i<n; i++) cn[x[i]=r[i]] ++ ;
    for(i=1; i<m; i++) cn[i] += cn[i-1] ;
    for(i=n-1;i>=0;i--) sa[ --cn[x[i]] ] = i ;
    for(k=1,p=1;p<n;k<<=1,m=p) {
        for(i=n-k,p=0 ; i<n; i++) y[p++] = i;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i] - k ;
        for(i=0;i<n;i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0;
        for(i=0;i<n;i++) cn[ky[i]]++ ;
        for(i=1;i<m;i++) cn[i]+=cn[i-1];
        for(i=n-1;i>=0;i--) sa[--cn[ky[i]]] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,p=1,i=1; i<n; i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0; i<n; i++) rank[sa[i]] = i ;
    int k=0;
    for(int i=0;i<n;i++){
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}
bool check(int limit ,int n) {
    int p = 1 ;
    for(int i=2; i<n; i++) {
        for(;i<n && height[i]>=limit; i++) ;
        int L = INF , R = -INF ;
        for(int j=p; j<i ;j++) {
            L = min(L , sa[j]) ;
            R = max(R , sa[j]) ;
            if(R - L >= limit+1) return true ;
        }
        p=i ;
    }
    return false ;
}

int main()
{ 
    int N  ;
    while(scanf("%d" ,&N)==1 && N) {
        int n = 0 ;
        for(int i=0; i<N ;i++) scanf("%d", &s[i]) ;
        if(N<10) { printf("0\n") ; continue ;}
        for(int i=1; i<N ;i++) {
            r[n++] = s[i] - s[i-1] + 90 ;
        }
        r[n++] = 0 ;
        build_sa(r,sa,n,200) ;
        cal_height(r ,n) ;

        int L = 0 , R = N/2;
        while(L < R) {
            int mid = L + (R-L+1)/2 ;
            if(check(mid , n)) L = mid ;
            else R = mid - 1 ;
        }
        L++ ;
        if(L<5) L = 0 ;
        printf("%d\n" , L) ;
    }
    return 0;
}

4. POJ 2774 Long Long Message (论文2.3.1 两个字符串,最长公共子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200050 ;

char buf[maxn] ;
int r[maxn], wa[maxn] , wb[maxn] , ky[maxn] , cn[maxn] ;
int sa[maxn] ,height[maxn] ,rank[maxn], id[maxn] ;

inline bool cmp(int *r,int a,int b,int l){
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void build_sa(int *r ,int *sa ,int n ,int m){
    int i,k,p,*x=wa,*y=wb ;
    for(i=0; i<m; i++) cn[i] = 0;
    for(i=0; i<n; i++) cn[x[i]=r[i]]++ ;
    for(i=1; i<m ;i++) cn[i] += cn[i-1] ;
    for(i=n-1 ;i>=0 ;i--) sa[ --cn[x[i]] ] = i ;
    for(k=1,p=1 ;p<n; k<<=1,m=p) {
        for(i=n-k , p=0 ;i<n;i++) y[p++] = i ;
        for(i=0; i<n ;i++) if(sa[i]>=k) y[p++] = sa[i] - k ;
        for(i=0 ;i<n; i++) ky[i] = x[ y[i] ] ;
        for(i=0; i<m; i++) cn[i] = 0;
        for(i=0 ;i<n; i++) cn[ky[i]] ++ ;
        for(i=1; i<m; i++) cn[i] += cn[i-1] ;
        for(i=n-1 ;i>=0 ;i--) sa[ --cn[ky[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],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    int k=0 ;
    for(int i=0; i<n; i++) rank[sa[i]] = i ;
    for(int i=0; i<n; i++) {
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k;
    }
}

int main()
{
    int n = 0 , mz = 'z'+1 ;
    for(int i=1; i<=2;i++) {
        gets(buf);
        for(int j=0; buf[j] ; j++){
            id[n] = i ;
            r[n++] = buf[j] ;
        }
        id[n] = i ;
        r[n++] = mz++ ;
    }
    id[n] = -1 ;
    r[n++] = 0;
    build_sa(r , sa, n , mz) ;
    cal_height(r , n) ;

    int ans = 0;
    for(int i=2; i<n; i++) if(id[sa[i]]-id[sa[i-1]]){
        ans = max(ans , height[i]) ;
    }

    printf("%d\n" , ans) ;
    return 0;
}


5.POJ 3261 Milk Patterns(论文2.21  单个字符串 可重叠的 k 次最长重复子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 20050 ;

int N , K ;
struct Node{int v ,id ; };
bool cmp_v(const Node &a , const Node&b){return a.v<b.v; }
Node DT[maxn] ;

int r[maxn],wa[maxn] ,wb[maxn],ky[maxn],cn[maxn];
int sa[maxn],rank[maxn],height[maxn];

inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb ;
    for(i=0;i<m;i++) cn[i]=0;
    for(i=0;i<n;i++) cn[ x[i]=r[i] ]++;
    for(i=1;i<m;i++) cn[i]+=cn[i-1] ;
    for(i=n-1;i>=0;i--) sa[--cn[x[i]] ] = i;
    for(k=1,p=1; p<n; k<<=1,m=p){
        for(i=n-k,p=0;i<n;i++) y[p++] = i;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i]-k;
        for(i=0;i<n;i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] =0;
        for(i=0;i<n;i++) cn[ky[i]]++;
        for(i=1;i<m;i++) cn[i]+=cn[i-1];
        for(i=n-1;i>=0;i--) sa[--cn[ky[i]] ] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,i=1,p=1; i<n;i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i ;
    int k=0;
    for(int i=0;i<N;i++){
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}
bool check(int len) {
    int beg = 1 ;
    for(int i=2;i<N;i++) {
        for(;i<N && height[i]>=len;i++) ;
        if(i-beg >=K) return true ;
        beg = i ;
    }
    return false ;
}

int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d" ,&N , &K) ; 
    for(int i=0; i<N; i++) scanf("%d",&DT[i].v) , DT[i].id = i;
    sort(DT,DT+N,cmp_v);
    ky[0]=1;
    int i,p;
    for(i=1,p=2; i<N;i++) ky[i] = DT[i].v==DT[i-1].v ? p-1 : p++ ;
    for(i=0;i<N;i++)    r[DT[i].id] = ky[i];
    r[N++]=0;
    build_sa(r,sa,N,p);
    cal_height(r ,N) ;
    int L = 0 , R = N ;
    while(L<R){
        int mid = L + (R-L+1)/2 ;
        if(check(mid))  L = mid  ;
        else R = mid-1 ;
    }
    printf("%d\n" , L);

    return 0;
}


6. POJ 3294 Life Forms  (和UVA 11107 是同一题,原来写法TLE,重写了一份)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
const int maxn = 110 * 1011 ;

char buf[maxn] ;
int r[maxn] ;
int sa[maxn], height[maxn] ,rank[maxn] , id[maxn] ;
int wa[maxn] ,wb[maxn], ky[maxn], cn[maxn] ;
inline bool cmp(int *r ,int a,int b,int l){
    return r[a]==r[b] && r[a+l] == r[b+l] ;
}
void build_sa(int *r, int *sa ,int n , int m){
    int i, k , p , *x=wa ,*y = wb;
    for(i=0; i<m; i++) cn[i] = 0;
    for(i=0; i<n; i++) cn[ x[i] = r[i] ] ++ ;
    for(i=1; i<m; i++) cn[i] += cn[i-1] ;
    for(i=n-1; i>=0; i--) sa[ --cn[ x[i] ] ] = i ;
    for(k=1,p=1; p<n; k<<=1 , m=p) {
        for(p=0,i=n-k; i<n; i++) y[p++] = i ;
        for(i=0; i<n; i++) if(sa[i] >=k ) y[p++] = sa[i] - k ;
        for(i=0; i<n; i++) ky[i] = x[ y[i] ] ;
        for(i=0; i<m; i++) cn[i] = 0;
        for(i=0; i<n; i++) cn[ky[i]]++ ;
        for(i=1; i<m; i++) cn[i] += cn[i-1] ;
        for(i=n-1; i>=0; i--) sa[ --cn[ky[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],k) ? p-1 : p++ ;
    }
    return ;
}
void get_height(int *s, int n) {
    int i, j , k=0;
    for(i=0; i<n; i++) rank[sa[i]] = i ;
    for(i=0; i<n-1; i++) {
        if(k) k-- ;
        j = sa[rank[i]-1] ;
        while(s[i+k] == s[j+k]) k++ ;
        height[rank[i]] = k;
    }
}

int INC = 1;
int flag[maxn] ;

bool check(int n ,int m, int mid) {
    INC += 110 ;
    int cnt = 1 ;  flag[ id[sa[1]] ] = INC ;
    for(int i=2; i<n; i++) {
        while(i<n && height[i] >= mid) {
            int who = id[sa[i]] ;
            if(flag[who] != INC) flag[who]=INC , cnt++ ;
            i++ ;
        }
        if(cnt*2 > m) return true ;
        INC += 110 , cnt = 1 ;
        flag[id[sa[i]]] = INC ;
    }
    return false ;
}
void print(int n, int m, int p) {
    INC += 110 ;
    int cnt = 1 ;  flag[ id[sa[1]] ] = INC ;
    for(int i=2; i<n; i++) {
        while(i<n && height[i] >= p) {
            int who = id[sa[i]] ;
            if(flag[who] != INC) flag[who]=INC , cnt++ ;
            i++ ;
        }
        if(cnt*2 > m) {
            int a = sa[i-1] ;
            for(int j=0; j<p ;j++) putchar(r[a+j]) ;
            puts("") ;
        }
        INC += 110 , cnt = 1 ;
        flag[id[sa[i]]] = INC ;
    }
}

int main()
{ 
    int m , first = 1 ;
    while(scanf("%d" ,&m)==1 && m) {  getchar() ;
        if(!first) puts("") ; first = 0;
        int n = 0;
        for(int i=1; i<=m; i++) {
            gets(buf);
            for(int j=0; buf[j] ; j++) {
                id[n]=i;
                r[n++] = buf[j] ;
            }
            id[n] = i;
            r[n++] = 'z' + i ;
        }
        if( m==1 ){
            puts(buf) ;
            continue ;
        }
        id[n] = m+1 , r[n++] = 0 ;
        build_sa(r , sa , n, 'z'+m+2) ;
        get_height(r , n) ;


        if(!check(n , m , 1)) {
            puts("?") ;
            continue ;
        }
        int L = 1 , R = 1000;
        while(L<R) {
            int mid = L + (R-L+1)/2 ;
            if(check(n , m, mid)) L = mid ;
            else R = mid -1 ;
        }
        print(n ,m ,L) ;
    }
    return 0;
}


7 . POJ 3415 Common Substrings (论文2.3.2  两个字符串  长度不小于 k 的公共子串的个数 , 单调栈优化)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
const int maxn = 200050 ;
typedef long long LL;

int K ;
char buf[maxn] ;
int r[maxn] ,wa[maxn],wb[maxn],ky[maxn],cn[maxn];
int height[maxn],rank[maxn],sa[maxn],id[maxn];

inline bool cmp(int *r,int a,int b , int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb ;
    for(i=0;i<m;i++) cn[i] = 0;
    for(i=0;i<n;i++) cn[x[i]=r[i]]++ ;
    for(i=1;i<m;i++) cn[i]+=cn[i-1];
    for(i=n-1;i>=0;i--) sa[ --cn[x[i]] ] = i ;
    for(k=1,p=1 ;p<n; k<<=1 , m=p){
        for(i=n-k,p=0; i<n; i++) y[p++] = i;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i]-k;
        for(i=0;i<n;i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0;
        for(i=0;i<n;i++) cn[ky[i]]++ ;
        for(i=1;i<m;i++) cn[i]+=cn[i-1];
        for(i=n-1;i>=0;i--) sa[ --cn[ky[i]] ] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,p=1,i=1; i<n; i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r ,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i;
    int k=0 ;
    for(int i=0;i<n;i++) {
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k]==r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}

int h[maxn],cnt[maxn],top ;

LL cal_ans(int n, int p ,int q){
    LL ans = 0;
    LL sum = top = 0;
    for(int i=2; i<n; i++){
        for(; i<n && height[i]>=K;i++){
            int _cnt = 0;
            for(;top>0 && height[i]<h[top];top--) {
                _cnt += cnt[top] ;
                sum -= (LL) cnt[top] * (h[top]+1-K);
            }
            if(id[sa[i-1]] == p || _cnt>0) {
                cnt[++top] = _cnt + ( id[sa[i-1]]==p ? 1 : 0) ;
                h[top] = height[i] ;
                sum += (LL) cnt[top] * ( h[top]+1-K) ;
            }
            if(id[sa[i]]==q ) {
                ans += sum ;
            }
        }
        sum = top = 0;
    }
    return ans ;
}

int main()
{ 
    while(scanf("%d",&K)==1 && K){
        int mz = 'z'+1 , n = 0 ;
        scanf("%s",buf);
        for(int i=0;buf[i];i++) id[n]=1 ,r[n++]=buf[i] ;
        id[n]=0,r[n++]=mz++ ;
        scanf("%s" ,buf) ;
        for(int i=0;buf[i];i++) id[n] = 2 ,r[n++] = buf[i];
        id[n] = 0 , r[n++] = 0 ;

        build_sa(r,sa,n,mz);
        cal_height(r,n);

        LL ans = 0;
        ans += cal_ans(n,1,2);
        ans += cal_ans(n,2,1);

        printf("%I64d\n" , ans);
    }
    return 0 ;
}


 8.  POJ 3693 Maximum repetition substring(论文2.2.4  单个字符串 重复次数最多的连续重复子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=100050 ;

int r[maxn] ;  char buf[maxn] ;
int wa[maxn] ,wb[maxn],ky[maxn] , cn[maxn] ;
int height[maxn], rank[maxn] ,sa[maxn] ;

inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb;
    for(i=0;i<m;i++) cn[i] = 0;
    for(i=0;i<n;i++) cn[x[i]=r[i]]++ ;
    for(i=1;i<m;i++) cn[i] += cn[i-1];
    for(i=n-1;i>=0;i--) sa[--cn[x[i]] ] = i;
    for(k=1,p=1 ;p<n;k<<=1 , m=p){
        for(i=n-k,p=0 ; i<n ;i++) y[p++]=  i ;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i] - k ;
        for(i=0;i<n; i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0 ;
        for(i=0;i<n;i++) cn[ky[i]] ++ ;
        for(i=1;i<m;i++) cn[i] += cn[i-1] ;
        for(i=n-1;i>=0;i--) sa[ --cn[ky[i]] ] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,i=p=1; i<n;i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i;
    int k =0 ;
    for(int i=0; i<n; i++) {
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}

int Log[maxn];
int best[20][maxn];
int rmq[maxn][20] ;
void rmq_init(int n){
    for(int i=0; i<n; i++) rmq[i][0] = height[i] ;
    for(int j=1; (1<<j)<=n; j++)
        for(int i=0; i+(1<<j)-1 <n ; i++)
            rmq[i][j] = min(rmq[i][j-1] , rmq[i+(1<<(j-1))][j-1]) ;
}
inline int ask(int L,int R){
    int t = Log[R-L+1] ;
    return min(rmq[L][t] , rmq[R-(1<<t)+1][t]) ;
}
inline int lcp(int a,int b){
    a = rank[a] , b=rank[b];
    if(a>b) swap(a,b) ;
    return ask(a+1,b);
}

void cal_ans(int n){
    n-- ;
    int max_rep=0 , len , ans_beg , ans_len ;
    for(int L=1; L<n; L++){
        if(L * max_rep >= n) break ;
        for(int i=0; i+L<n ;i+=L){
            int k = lcp(i , i+L) ;
            int tmp = k / L ;
            int ii = i - (L - k%L);
            if(ii>=0 && lcp(ii , ii+L) >= L-k%L) tmp++ ;
            tmp++ ;
            if(tmp > max_rep) max_rep = tmp , len = L ;
        }
    }
    ans_beg = -1 , ans_len = max_rep * len ;
    for(int i=0; i+ans_len-1 < n; i++) {
        if(lcp(i , i+len) >= ans_len - len) {
            if(ans_beg == -1) ans_beg = i ;
            else if(rank[i] < rank[ans_beg]) ans_beg = i ;
        }
    }
    for(int i=ans_beg ; i<ans_beg + ans_len ;i++) putchar(buf[i]) ;
    puts("") ;
}

int main()
{
   // freopen("in.txt","r",stdin);
    Log[0] = -1;
    for(int i=1;i<maxn;i++){
        Log[i]=(i&(i-1))?Log[i-1]:Log[i-1] + 1 ;
    }
    int cas = 1 ;
    while(gets(buf) && buf[0]!='#'){
        int n = 0;
        for(int i=0; buf[i]; i++) r[n++] = buf[i] ;
        r[n++] = 0 ;
        build_sa(r ,sa ,n ,'z'+1);
        cal_height(r ,n) ;
        rmq_init(n) ;

        printf("Case %d: " , cas++) ;
        cal_ans(n) ;
    }
    return 0;
}

9. SPOJ DISUBSTR Distinct Substrings(论文2.2.2  单个字符串  不相同子串个数)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1010 ;

char r[maxn] ;
int wa[maxn] , wb[maxn] ,ky[maxn] , cn[maxn];
int height[maxn] , rank[maxn] , sa[maxn];

inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(char *r ,int *sa ,int n ,int m){
    int i, k , p ,*x =wa ,*y = wb ;
    for(i=0; i<m; i++) cn[i]=  0 ;
    for(i=0; i<n; i++) cn[x[i]=r[i]]++ ;
    for(i=1; i<m; i++) cn[i] += cn[i-1] ;
    for(i=n-1 ;i>=0 ;i--) sa[--cn[x[i]]] = i ;
    for(k=p=1; p<n ;m=p , k<<=1){
        for(p=0,i=n-k;i<n;i++) y[p++] = i ;
        for(i=0;i<n;i++) if(sa[i] >= k) y[p++] = sa[i] - k ;
        for(i=0;i<n;i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0;
        for(i=0;i<n;i++) cn[ky[i]] ++ ;
        for(i=1;i<m;i++) cn[i] += cn[i-1] ;
        for(i=n-1 ;i>=0; i--) sa[--cn[ky[i]]] = y[i];
        for(swap(x,y),x[sa[0]]=0 , i=p=1;i<n;i++)
        x[sa[i]] = cmp(y ,sa[i-1] ,sa[i] ,k) ? p-1 : p++ ;
    }
}
void cal_height(char *r , int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i ;
    int k=0;
    for(int i=0;i<n;i++){
        if(k) k-- ;
        int j = sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}
int cal_ans(int n){
    n-- ;
    int ans = 0 ;
    for(int i=1; i<=n; i++) {
         ans += n -sa[i] - height[i] ;
    }
    return ans ;
}
int main()
{
    int T;
    scanf("%d" ,&T) ;
    while(T--) {
        scanf("%s" ,r) ;
        int n = strlen(r)+1 ;
        build_sa(r ,sa ,n , 'z'+1) ;
        cal_height(r , n);
        int ans = cal_ans(n) ;
        printf("%d\n" ,ans) ;
    }
    return 0;
}

10. SPOJ PHRASES Relevant Phrases of Annihilation(多个字符串 每个字符串至少出现两次且不重叠的最长子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1e6 ;
const int maxn = 100050 ;

char buf[maxn];
int r[maxn] ,wa[maxn],wb[maxn] ,ky[maxn] ,cn[maxn] ;
int height[maxn],rank[maxn],sa[maxn] ,id[maxn] ;


inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb;
    for(i=0;i<m;i++) cn[i] = 0;
    for(i=0;i<n;i++) cn[x[i]=r[i]]++ ;
    for(i=1;i<m;i++) cn[i] += cn[i-1];
    for(i=n-1;i>=0;i--) sa[--cn[x[i]] ] = i;
    for(k=1,p=1 ;p<n;k<<=1 , m=p){
        for(i=n-k,p=0 ; i<n ;i++) y[p++]=  i ;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i] - k ;
        for(i=0;i<n; i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0 ;
        for(i=0;i<n;i++) cn[ky[i]] ++ ;
        for(i=1;i<m;i++) cn[i] += cn[i-1] ;
        for(i=n-1;i>=0;i--) sa[ --cn[ky[i]] ] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,i=p=1; i<n;i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i;
    int k =0 ;
    for(int i=0; i<n; i++) {
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}

int mi[12] , ma[12] , cnt[12] ;
void init_check(){
    for(int i=0; i<12; i++) mi[i] = INF , ma[i]=-INF ,cnt[i] = 0;
}
bool check(int len ,int n , int N){
    init_check();
    int p = sa[1] ;
    mi[id[p]] = min(mi[id[p]] , sa[1]) ;
    ma[id[p]] = max(ma[id[p]] , sa[1]) ;
    cnt[id[p]]++ ;
    for(int i=2; i<n; i++){
        for(;i<n && height[i]>=len; i++){
            int p = sa[i] ;
            mi[id[p]] = min(mi[id[p]] , p) ;
            ma[id[p]] = max(ma[id[p]] , p) ;
            cnt[id[p]]++ ;
        }
        bool ok = true ;
        for(int j=1;j<=N;j++) {
            if(ma[j] - mi[j] < len || cnt[j]<2) {ok=false ; break; }
        }
        if(ok) return true ;
        init_check() ;
        int p = sa[i] ;
        mi[id[p]] = min(mi[id[p]] , p) ;
        ma[id[p]] = max(ma[id[p]] , p) ;
        cnt[id[p]]++ ;
    }
    return false ;
}

int main()
{ 
    int T;
    scanf("%d",&T);
    while(T--){
        int n = 0 , N  , mz ='z'+1  , Mlen =0 ;
        scanf("%d" ,&N);  gets(buf) ;
        for(int i=1;i<=N;i++){
            gets(buf) ;
            int j;
            for(j=0; buf[j] ;j++) if(buf[j]>='a' && buf[j]<='z') {
                id[n]=i ;
                r[n++] = buf[j] ;
            }
            Mlen = max(Mlen , j);
            id[n] =  0;
            r[n++] = mz++;
        }
        id[n] = 0 , r[n++] = 0 ;
        build_sa(r,sa,n,mz) ;
        cal_height(r,n) ;

        int L = 0 , R = Mlen ;
        while(L<R){
            int mid = L + (R-L+1)/2 ;
            if(check(mid , n ,N)) L = mid ;
            else R  = mid - 1 ;
        }
        printf("%d\n" , L) ;
    }
    return 0;
}

11. SPOJ REPEATS Repeats(重复次数最多的连续重复子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=100050 ;

int r[maxn] ;  char buf[maxn] ;
int wa[maxn] ,wb[maxn],ky[maxn] , cn[maxn] ;
int height[maxn], rank[maxn] ,sa[maxn] ;

inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l]==r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb;
    for(i=0;i<m;i++) cn[i] = 0;
    for(i=0;i<n;i++) cn[x[i]=r[i]]++ ;
    for(i=1;i<m;i++) cn[i] += cn[i-1];
    for(i=n-1;i>=0;i--) sa[--cn[x[i]] ] = i;
    for(k=1,p=1 ;p<n;k<<=1 , m=p){
        for(i=n-k,p=0 ; i<n ;i++) y[p++]=  i ;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i] - k ;
        for(i=0;i<n; i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0 ;
        for(i=0;i<n;i++) cn[ky[i]] ++ ;
        for(i=1;i<m;i++) cn[i] += cn[i-1] ;
        for(i=n-1;i>=0;i--) sa[ --cn[ky[i]] ] = y[i] ;
        for(swap(x,y),x[sa[0]]=0,i=p=1; i<n;i++)
        x[sa[i]] = cmp(y,sa[i-1],sa[i],k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i;
    int k =0 ;
    for(int i=0; i<n; i++) {
        if(k) k-- ;
        int j=sa[rank[i]-1] ;
        while(r[i+k] == r[j+k]) k++ ;
        height[rank[i]] = k ;
    }
}

int Log[maxn];
int best[20][maxn];
int rmq[maxn][20] ;
void rmq_init(int n){
    for(int i=0; i<n; i++) rmq[i][0] = height[i] ;
    for(int j=1; (1<<j)<=n; j++)
        for(int i=0; i+(1<<j)-1 <n ; i++)
            rmq[i][j] = min(rmq[i][j-1] , rmq[i+(1<<(j-1))][j-1]) ;
}
inline int ask(int L,int R){
    int t = Log[R-L+1] ;
    return min(rmq[L][t] , rmq[R-(1<<t)+1][t]) ;
}
inline int lcp(int a,int b){
    a = rank[a] , b=rank[b];
    if(a>b) swap(a,b) ;
    return ask(a+1,b);
}

void cal_ans(int n){
    n-- ;
    int max_rep=0 ;
    for(int L=1; L<n; L++){
        if(L * max_rep >= n) break ;
        for(int i=0; i+L<n ;i+=L){
            int k = lcp(i , i+L) ;
            int tmp = k / L ;
            int ii = i - (L - k%L);
            if(ii>=0 && lcp(ii , ii+L) >= L-k%L) tmp++ ;
            tmp++ ;
            if(tmp > max_rep) max_rep = tmp ;
        }
    }
    printf("%d\n" , max_rep) ;
}

int main()
{
   // freopen("in.txt","r",stdin);
    Log[0] = -1;
    for(int i=1;i<maxn;i++){
        Log[i]=(i&(i-1))?Log[i-1]:Log[i-1] + 1 ;
    }
    int  T ;
    scanf("%d" ,&T) ;
    while(T--){
        int n ;
        scanf("%d" , &n);
        for(int i=0; i<n; i++) scanf("%s" ,buf) , r[i] = buf[0] ;
        r[n++] = 0 ;
        build_sa(r ,sa ,n ,'z'+1);
        cal_height(r ,n) ;
        rmq_init(n) ;
        cal_ans(n) ;
    }
    return 0;
}

12 . URAL 1297Palindrome  (最长回文子串)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2050;

char buf[maxn] ;
int r[maxn],pos[maxn], N;
int wa[maxn],wb[maxn],ky[maxn],cn[maxn];
int sa[maxn],rank[maxn],height[maxn];

inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l] == r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb ;
    for(i=0;i<m;i++) cn[i]= 0;
    for(i=0;i<n;i++) cn[x[i]=r[i]] ++ ;
    for(i=1;i<m;i++) cn[i]+=cn[i-1];
    for(i=n-1;i>=0;i--) sa[--cn[x[i]]] = i ;
    for(k=p=1 ;p<n; k<<=1 , m=p){
        for(i=n-k,p=0; i<n; i++) y[p++] = i ;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i]- k;
        for(i=0;i<n;i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0;
        for(i=0;i<n;i++) cn[ky[i]]++ ;
        for(i=1;i<m;i++) cn[i] += cn[i-1] ;
        for(i=n-1;i>=0;i--) sa[--cn[ky[i]]] = y[i] ;
        for(swap(x,y),x[sa[0]]=0 , i=p=1; i<n;i++)
        x[sa[i]] = cmp(y , sa[i-1] ,sa[i] , k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i ;
    int k=0;
    for(int i=0;i<n;i++) {
        if(k) k-- ;
        int j= sa[rank[i]-1] ;
        while(r[i+k] == r[j+k])
            k++ ;
        height[rank[i]] = k ;
    }
}

int rmq[maxn][20] ;
void init_rmq(int n){
    for(int i=0; i<n; i++) rmq[i][0] = height[i] ;
    for(int j=1;(1<<j)<=n; j++){
        for(int i=0; i+(1<<j)-1<n ;i++){
            rmq[i][j] = min(rmq[i][j-1] , rmq[i+(1<<j>>1)][j-1]) ;
        }
    }
}
int ask(int L,int R){
    int k = 0 ;
    while((1<<(k+1)) <= R-L+1 ) k++ ;
    return min(rmq[L][k] , rmq[R-(1<<k)+1][k]) ;
}
int lcp(int a , int b){
    a = rank[a] , b = rank[b] ;
    if(a > b) swap(a ,b) ;
    return ask(a+1 , b) ;
}

void cal_ans(int n){
    int p = 0 , M = 0 ;
    for(int i=0 ; i<N; i++){
        int tmp = lcp(i , N*2-i+1) ;
        if(tmp*2 > M) M = tmp*2 , p = i - tmp ;
        tmp = lcp(i , N*2-i) ;
        if(tmp*2-1 > M) M = tmp*2-1 , p = i - tmp + 1 ;
    }
    buf[p+M] = 0 ;
    printf("%s\n" , buf+p) ;
}

int main()
{ 
    while(scanf("%s" ,buf)!=EOF){
        N= strlen(buf) ;
        int n = 0 , mz = 'z'+1 ;
        for(int i = 0 ; i<N; i++){
            pos[n] = i ;
            r[n++] = buf[i] ;
        }
        pos[n]=-1 , r[n++] = mz++ ;
        for(int i=N-1 ; i>=0 ;i--){
            pos[n] = i ;
            r[n++]=buf[i] ;
        }
        pos[n]=-1, r[n++] = 0 ;
        build_sa(r,sa,n,mz) ;
        cal_height(r,n);
        init_rmq(n) ;
        cal_ans(n) ;
    }
    return 0;
}


13. URAL 1517 Freedom of Choice  (最长公共子序列)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200050 ;

char buf[maxn] ;
int r[maxn] , N ;
int wa[maxn],wb[maxn],ky[maxn],cn[maxn];
int sa[maxn],rank[maxn],height[maxn];

inline bool cmp(int *r,int a,int b,int l){
    return r[a]==r[b] && r[a+l] == r[b+l] ;
}
void build_sa(int *r,int *sa,int n,int m){
    int i,k,p,*x=wa,*y=wb ;
    for(i=0;i<m;i++) cn[i]= 0;
    for(i=0;i<n;i++) cn[x[i]=r[i]] ++ ;
    for(i=1;i<m;i++) cn[i]+=cn[i-1];
    for(i=n-1;i>=0;i--) sa[--cn[x[i]]] = i ;
    for(k=p=1 ;p<n; k<<=1 , m=p){
        for(i=n-k,p=0; i<n; i++) y[p++] = i ;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++] = sa[i]- k;
        for(i=0;i<n;i++) ky[i] = x[y[i]] ;
        for(i=0;i<m;i++) cn[i] = 0;
        for(i=0;i<n;i++) cn[ky[i]]++ ;
        for(i=1;i<m;i++) cn[i] += cn[i-1] ;
        for(i=n-1;i>=0;i--) sa[--cn[ky[i]]] = y[i] ;
        for(swap(x,y),x[sa[0]]=0 , i=p=1; i<n;i++)
        x[sa[i]] = cmp(y , sa[i-1] ,sa[i] , k) ? p-1 : p++ ;
    }
}
void cal_height(int *r,int n){
    for(int i=0;i<n;i++) rank[sa[i]] = i ;
    int k=0;
    for(int i=0;i<n;i++) {
        if(k) k-- ;
        int j= sa[rank[i]-1] ;
        while(r[i+k] == r[j+k])
            k++ ;
        height[rank[i]] = k ;
    }
}
void cal_ans(int n){
    int M = 0 , p = 0 ;
    for(int i=2; i<n; i++) {
        if( (sa[i-1]<N) != (sa[i]<N) ) {
            if(height[i] > M) M = height[i] , p = max( sa[i] , sa[i-1] ) ;
        }
    }
    p-=N+1 ;
    buf[p+M] = 0 ;  //cout<<M<<"  "<<p<<endl ;
    printf("%s\n" , buf+p);
}


int main()
{ 
    int mz = 'z'+1 , n = 0;
    scanf("%d" ,&N) ;
    scanf("%s" ,buf) ;
    for(int i=0;i<N;i++) r[n++] = buf[i] ;
    scanf("%s" ,buf) ;
    r[n++] = mz++ ;
    for(int i=0;i<N;i++) r[n++] = buf[i] ;
    r[n++] = 0 ;
    build_sa(r ,sa,n,mz) ;
    cal_height(r , n) ;
    cal_ans(n);
    return 0;
}



14.  敲完收工。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值