后缀数组(sort版)

以下内容均取自挑战程序设计竞赛

目录

 

1.后缀数组的计算:

2.基于后缀数组的字符串匹配:

3.高度数组(LCP  Array)

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).计算最长回文子串。例题

猜你喜欢

转载自blog.csdn.net/qq_40679299/article/details/82145742