后缀数组学习笔记

后缀数组倍增预处理

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#define N 2200000
#define L 2000000
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
    char ch=0;
    int x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
char s[N];
int n,m;
int c[N],x[N],y[N],sa[N];
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);m=122;
    for(int i=1;i<=n;i++)++c[x[i]=s[i]];
    for(int i=1;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<<=1)
    {
    	//sa[i]当前是按照第一关键字排序的第i名的后缀编号
    	//y[i]是第二关键字的排名为i的数,它第一关键字的位置,根据定义,下面是sa[i]-k 
        int num=0;
        for(int i=n-k+1;i<=n;i++)y[++num]=i;
        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=1;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;
		//按照第二关键字的顺序倒着取
		//先看一下这个第二关键字对应的第一关键字的位置
		//用x来确定一下排名它当前的排名
		//用桶来算出它在下一次会排在第几名
		//根据sa数组的定义,在下一轮中,sa[这个排名]=第一关键字的位置 
		swap(x,y);//只是为了避免多开一个数组,利用y来做一个x的拷贝而已 
		x[sa[1]]=num=1;
		for(int i=2;i<=n;i++)
		x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?num:++num; 
		//这里的y数组是之间的x数组 
		//分别比较这一个和上一个的两个关键字是否同时相同。 
		if(num==n)break;
		m=num; 
    }
    for(int i=1;i<=n;i++)printf("%d ",sa[i]);
    return 0;
}

处理height数组

height[i]=lcp(sa[i],sa[i-1])
为了得到height数组,可以先处理出一个h数组
h[i]=height[rank[i]]
由此可以推出height[i]=h[sa[i]]
考虑暴力计算这个h[i]数组是O(n^2)的
但这里有一个很重要的性质,即h[i]>=h[i-1]-1
手动模拟一下即可证明。
因此,这个东西可以O(n)来求。

void calheight()
{
	for(int i=1,k=0;i<=n;i++)
	{
		if(k)k--;
		int j=sa[rank[i]-1];
		while(s[i+k]==s[j+k])k++;
		height[rank[i]]=k;
	}
}

其中这个k就代表了h[i]的意思

原文地址:https://www.cnblogs.com/Creed-qwq/p/10096145.html