后缀数组(基数排序)

不理解后缀数组的童鞋,可以看看挑战程序设计书上讲的,非常好,不过那上面讲的使是用sort进行的排序,效率较基数排序低。不过思想一样,那上面看懂了看这个很快。

说三点:

(1)y[]数组表示按照第二关键字排号的情况,目前里面存的时第一关键字的值,比如  y[1]=4,表示第二关键字排在第1位上此时第一关键字为下标从4开始的子字符串。因此用基数排序,因为其相当稳定。

(2)swap交换后,计算x此时x就代表rank数组,相当于rank[sa[i]]=i,因为存在相等的子串,所以要判断一下。

(3)排好序的时候,因为没有后缀再相同,所以此时p>=n

附加一份我理解过程中输出中间结果的代码:https://paste.ubuntu.com/p/fcDYPzpCbD/,自己动手理解的更快。

计蒜客的另外一份模板,没有下面这个好理解,但先放着把,毕竟网上好多人用的都是这个。

#include <iostream>
#include <string.h>
using namespace std;

const int MAX_N=210000;
char s[MAX_N];
int sa[MAX_N],t[MAX_N],t2[MAX_N],c[MAX_N],n;

void build_sa(int m){
    int i,*x=t,*y=t2;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++) c[x[i]=s[i]]++;
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1){
        int p=0;
        for(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<m;i++) c[i]=0;
        for(i=0;i<n;i++) c[x[y[i]]]++;
        for(i=1;i<m;i++) c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if(p>=n) break;
        m=p;
    }
}

int Rank[MAX_N],h[MAX_N];
void get_h(){
	int i,j,k=0;
    for(i=0;i<n;i++) Rank[sa[i]]=i;
    for(i=0;i<n;i++){
        if(k) k--;
        if(Rank[i]){
            j=sa[Rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            h[Rank[i]]=k;
        }
    }
}

int main() {
    cin>>s;
    n=strlen(s);
    build_sa(131);
    get_h();
    return 0;
}

单子符串问题:

不可重重叠最长重复子串

最长k次重复子串

不相同子串的个数  

字符串中字典序第K小的子串

不可重叠重复子串的个数

两个字符串问题:

最长公共子串

长度不小于k的公共子串个数(可以相同)

多个字符串问题:

猜你喜欢

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