【後綴數組】不可重疊最長重複子串

hz2016評測《《點擊訪問

caioj《《點擊訪問

這題陰森可怕,兩個後綴可以不相同?哦天哪,怎麼判斷呢。

於是,某個聰明的人發現,後綴中兩兩字符的ANSI值的差是相等的,於是我們可以維護一個由差值組成的後綴數組。所以呢,我們就可以用後綴數組解這道題了,但是這一題,我們還要引入一個新的概念height數組和h數組。

height數組的定義就是排名爲 i 與 i-1 的最長公共前綴。

h就是代表排名爲 i  的後綴與其前一個後綴的最長前綴
#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Maxchar 28192 //20000 +2^13
#define Maxs 30
#define mes(x,y) memset(x,y,sizeof(x));
#define mpy(x,y) memcpy(x,y,sizeof(x))
#define INF 2147483647
using namespace std; 
int n,a[Maxchar+1],tt[Maxchar+1],Rank[Maxchar+1],sa1[Maxchar+1],Rank2[Maxchar+1],sa2[Maxchar+1],Rsort[Maxchar+1];
void get_sa(int n,int m){
    memcpy(Rank,a,sizeof(Rank));
    memset(Rsort,0,sizeof(Rsort));
    for(int i=1;i<=n;i++)Rsort[Rank[i]]++;
    for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
    for(int i=n;i>=1;i--)sa1[Rsort[Rank[i]]--]=i;
    int ln=1,p=0;
    while(p<n){
        int k=0;
        for(int i=n-ln+1;i<=n;i++)sa2[++k]=i;
        for(int i=1;i<=n;i++)if(sa1[i]-ln>0)sa2[++k]=sa1[i]-ln;
        memset(Rsort,0,sizeof(Rsort));
        for(int i=1;i<=n;i++)Rsort[Rank[i]]++;
        for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1]; 
        for(int i=n;i>=1;i--)sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];
        for(int i=1;i<=n;i++)tt[i]=Rank[i];
        p=1;Rank[sa1[1]]=1;
        for(int i=2;i<=n;i++){
            if(tt[sa1[i]]!=tt[sa1[i-1]]||tt[sa1[i]+ln]!=tt[sa1[i-1]+ln])p++;
            Rank[sa1[i]]=p;
        } 
        m=p;ln*=2;
    }
} 
//上面是基礎的後綴數組模版。 
int height[Maxchar+1];
void get_he(int n){
    int j,k=0;
    for(int i=2;i<=n;i++){
        j=sa1[Rank[i]-1];
        if(k!=0)k--; 
        while(a[j+k]==a[i+k])k++;//匹配最初前綴 
        //h數組的定義就是後綴 i 和後綴 i-1的最長前綴 
        height[Rank[i]]=k;//記錄兩個後綴的最長前綴 
        //最後記錄下排名爲 i 的後綴與其前一個後綴的最長前綴 
    }
}
//就是新的h數組,這個纔是後綴數組的真諦。 
bool check(int k,int n){
    for(int i=2;i<=n;i++){
        if(height[i]>=k){
            for(int j=i-1;j>=2;j--){
                if(abs(sa1[i]-sa1[j])>=k)return true;//滿足直接true 
                if(height[j]<k)break;//二分假如現在條件不滿足就跳出 
            }
        }
    }
    return false;
}
int main(){
	while(scanf("%d",&n)!=EOF){
		if(n==0)break;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		int mmax=-9999999;
        for(int i=1;i<n;i++){ 
            a[i]=a[i+1]-a[i]+88;
            if(mmax<a[i])mmax=a[i];
            //取maxx是爲了減少後綴數組的基數排序時間複雜度 
        }
        a[n]=0;n--;
        get_sa(n,mmax);get_he(n);
        //然後通過二分枚舉答案 
        int l=1,r=n,ans=1;
        while(l<=r){
            int mid=(l+r)/2;
            if(check(mid,n)==true){//二分枚舉長度 
                ans=mid;
				l=mid+1;
            }
            else r=mid-1;
        }
        if(ans<4)printf("0\n"); 
        else printf("%d\n",ans+1);
	}
    return 0;
}
 

查看原文:http://hz2016.tk/blog/?p=29
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章