学了后缀自动机,切了寥寥几道字符串题发现就切不动了,问了sw和yzy大佬说其他题大多要用后缀数组,原来觉得学了后缀自动机就不用后缀数组这玩意了,没想到它也是很有用的,没办法再去恶补一波。。
后缀数组就是搞出字符串所有后缀给它排个序,sa[i]表示排第i的是哪个,rnk[i]表示i排哪,显然这两个是互逆的,求sa就能求rnk了
sa的求法主要就是一个倍增算法,算出某一长度的更新该长度乘2的。不过其中恶心的是直接用sort复杂度nloglog爆炸,一定要手写桶排来搞双关键字排序保证复杂度是nlog,导致这玩意思想很好懂代码贼难写,网上看了几个板子,学着写了一个看起来不错的。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int x[N],rnk[N],sa[N],y[N],tax[N],n,m;
char s[N];
void rsort()//双关键字排序,x当前在排的第一关键字,y第二关键字排名为i的位置
{
for(int i=1;i<=m;i++)tax[i]=0;
for(int i=1;i<=n;i++)tax[x[i]]++;
for(int i=1;i<=m;i++)tax[i]+=tax[i-1];
for(int i=n;i>=1;i--)sa[tax[x[y[i]]]--]=y[i];
}
void cal_sa()
{
int num;
m=130;
for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i;//要注意y[i]表示的是第二关键字排第i的是哪
rsort();
for(int l=1;;l<<=1)//倍增
{
num=0;
for(int i=n-l+1;i<=n;i++)y[++num]=i;//这些位置第二维关键字不存在,第二维排名靠前
for(int i=1;i<=n;i++)
if(sa[i]>l)y[++num]=sa[i]-l;//按前一次所得名次得到这一次第二维排名
rsort();
swap(x,y);
x[sa[1]]=1,num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+l]==y[sa[i]+l])?num:++num;
if(num==n)break;
else m=num;
}
for(int i=1;i<=n;i++)rnk[sa[i]]=i;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
cal_sa();
}