《后缀数组》

1:后缀排序。

基于倍增和桶排的做法。

对于每个位置有第一关键字和第二关键字。

利用倍增来求解关键字,当所有人的排名都不一样时即完成。

2:LCP

最长公共前缀,非常有用。

洛谷:

P3809 【模板】后缀排序

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e6 + 5;
const int M = 1e3 + 5;
const LL Mod = 1000000;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

char s[N];
int x[N],y[N],c[N],sa[N],rk[N],height[N],n,m;
//x - 第一关键字,y - 第二关键字,c - 桶,sa - 排名为i的后缀编号的首位下标,rk - 编号为i的后缀的排名,height[i] = LCP(i,i - 1),LCP - 最长公共前缀
void SA() {
    for(int i = 1;i <= n;++i) x[i] = s[i],c[x[i]]++;//桶统计
    for(int i = 2;i <= m;++i) c[i] += c[i - 1];//前缀和
    for(int i = n;i >= 1;--i) sa[c[x[i]]--] = i;
    for(int k = 1;k <= n;k = (k << 1)) {//倍增
        int num = 0;
        for(int i = n - k + 1;i <= n;++i) y[++num] = i;//后k位不存在
        for(int i = 1;i <= n;++i) {
            if(sa[i] > k) {
                y[++num] = sa[i] - k;//可做其他的第二关键字
            }
        }
        for(int i = 1;i <= m;++i) c[i] = 0;
        for(int i = 1;i <= n;++i) c[x[i]]++;
        for(int i = 2;i <= m;++i) c[i] += c[i - 1];
        for(int i = n;i >= 1;--i) sa[c[x[y[i]]]--] = y[i],y[i] = 0;//更新sa
        swap(x,y);
        num = 1,x[sa[1]] = 1;
        for(int i = 2;i <= n;++i) {//更新x数组
            if(y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) x[sa[i]] = num;
            else x[sa[i]] = ++num;
        }
        if(num == n) break;//满足所有排名都不一样退出。
        m = num;
    }
    for(int i = 1;i <= n;++i) printf("%d%c",sa[i],i == n ? '\n' : ' ');
}
void LCP() {
    int k = 0;
    for(int i = 1;i <= n;++i) rk[sa[i]] = i;//初始化rk
    for(int i = 1;i <= n;++i) {
        if(rk[i] == 1) {
            height[1] = 0;
            continue;
        }
        if(k) k--;//更新k
        int j = sa[rk[i] - 1];
        while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
    }
    for(int i = 1;i <= n;++i) printf("%d%c",height[i],i == n ? '\n' : ' ');
}
int LCP(int x,int y){//LCP(x,y) = min(height[k]) (x < k <= y)
    int ans = INF;
    for(int i = x + 1;i <= y;++i) {
        ans = min(ans,height[i]);
    }
    return ans;
}
int main()
{
    scanf("%s",s + 1);
    n = strlen(s + 1);
    m = 122;
    SA();
    system("pause");
    return 0;
}
View Code

 

posted @ 2021-02-15 11:38  levill  阅读(55)  评论(0编辑  收藏  举报