1. 程式人生 > >字尾陣列(sort版)

字尾陣列(sort版)

以下內容均取自挑戰程式設計競賽

目錄

4.應用

1.字尾陣列的計算:

int n,k;
int rankk[maxn+1];
int tmp[maxn+1];

//sa[i]表示排在第i的字串起始字母的下標。
//rank[i]表示從第i位字母開始的字尾排在第幾。

bool compare_sa(int i,int j){
    if(rankk[i]!=rankk[j]){
        return rankk[i]<rankk[j];
    }else{
        int ri=i+k<=n?rankk[i+k]:-1;
        int rj=j+k<=n?rankk[j+k]:-1;
        return ri<rj;
    }
}

void construct_sa(string s,int *sa){
    n=s.size();
    for(int i=0;i<=n;i++){
        sa[i]=i;
        rankk[i]=i<n?s[i]:-1;
    }
    for(k=1;k<=n;k<<=1){
        sort(sa,sa+n+1,compare_sa);
        tmp[sa[0]]=0;
        for(int i=1;i<=n;i++){
            tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);
        }
        for(int i=0;i<=n;i++){
            rankk[i]=tmp[i];
        }
    }
}

2.基於字尾陣列的字串匹配:

KMP複雜度為O(|T|+|S|),字尾陣列為O(|T|log|S|),所以在|S|比較大時,字尾陣列更高效,同時對同一個字串多次匹配時,字尾陣列更高效。

bool contain(string s,int *sa,string t){
    int a=0,b=s.size();
    while(b-a>1){
        int c=(a+b)>>1;
        if(s.compare(sa[c],t.size(),t)<0) a=c;
        else b=c;
    }
    return s.compare(sa[b],t.length(),t)==0;
}

3.高度陣列(LCP  Array)

lcp[i]表示字尾S[sa[i]...]和字尾S[sa[i+1]...]的最長公共字首的長度位lcp[i]

void construct_lcp(string s,int *sa,int *lcp){
    int n=s.size();
    for(int i=0;i<=n;i++){
        rankk[sa[i]]=i;
    }
    int h=0;
    lcp[0]=0;
    for(int i=0;i<n;i++){
        int j=sa[rankk[i]-1];
        if(h) h--;
        for(;j+h<n&&i+h<n;h++){
            if(s[j+h]!=s[i+h]) break;
        }
        lcp[rankk[i]-1]=h;
    }
}

將rmq(區間查詢)與字尾陣列結合,可以計算任意兩個字尾的最長公共字首。

字尾S[i...], S[j...](rank[i]<rank[j])的公共字首長度為min(lcp[rank[i]], lcp[rank[i]+1], ... , lcp[rank[j]-1]). 

4.應用

1).字串匹配

2).計算最長公共子串。例題

3).計算最長迴文子串。例題