SA后缀数组
sa数组:sa[i]=x表示对于所有的后缀进行排序(字典序)后,得到排名为i的以第x个字符开头的后缀
rk数组:rk[x]=i,是对于所有的后缀进行排序(字典序)后,得到以第x个字符开头的排名为i的后缀
sa[rk[x]]=rk[sa[x]]=x
常用算法:nlogn的倍增算法
对于每一个点开始,从\(1+2^k\)的长度开始倍增,
相当于每次倍增都对于每一个两部分组合而成的二元组进行双关键字的排序
利用排名数组rk和oldrk不断更新,最终可以得出sa数组
用(nlogn)的排序算法,复杂度会多一只log,变成(nlog^2n)
模板
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int read() {
int x=0,f=1;
char ch=getchar();
while(ch<48||ch>47) {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>=48&&ch<=57) {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
struct Ranks {
int rk1,rk2;
} rk[N];
int sa[N],k;
char s[N];
bool cmp(int x,int y) {
if(rk[x].rk1==rk[y].rk1) return rk[x+k].rk1<rk[y+k].rk1;
return rk[x].rk1<rk[y].rk1;
}
int n;
void rerank() {
for(int i=0; i<=n; i++) {
rk[i].rk2=rk[i].rk1;
}
}
int main() {
cin>>(s+1);
n=strlen(s+1);
for(int i=1; i<=n; i++) {
sa[i]=i;
rk[i].rk1=s[i];
}
for(k=1; k<n; k<<=1) {
sort(sa+1,sa+n+1,cmp);
rerank();
for(int t=0,i=1; i<=n; i++) {
if(rk[sa[i]].rk2==rk[sa[i-1]].rk2&&rk[sa[i]+k].rk2==rk[sa[i-1]+k].rk2) rk[sa[i]].rk1=t;
else rk[sa[i]].rk1=++t;
}
}
for(int i=1; i<=n; i++) printf("%d ",sa[i]);
return 0;
}
计数排序优化版
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int n,k,id[N],cnt[N];
int sa[N],rk1[N],rk2[N];
char s[N];
void rerank() {
for(int i=1; i<=n; i++) {
rk2[i]=rk1[i];
}
}
int main() {
cin>>(s+1);
n=strlen(s+1);
int m=127,x=0;
for(int i=1; i<=n; i++) ++cnt[rk1[i]=s[i]];
for(int i=1; i<=m; i++) cnt[i]+=cnt[i-1];
for(int i=n; i>=1; i--) sa[cnt[rk1[i]]--]=i;
rerank();
for(int i=1; i<=n; i++) {
if(rk2[sa[i]]==rk2[sa[i-1]]) rk1[sa[i]]=x;
else rk1[sa[i]]=++x;
}
for(int k=1; k<n; k<<=1,m=n) {
memset(cnt,0,sizeof(cnt));
memcpy(id+1,sa+1,sizeof(sa));
for(int i=1; i<=n; i++) cnt[rk1[id[i]+k]]++;
for(int i=1; i<=m; i++) cnt[i]+=cnt[i-1];
for(int i=n; i>=1; i--) sa[cnt[rk1[id[i]+k]]--]=id[i];
memset(cnt,0,sizeof(cnt));
memcpy(id+1,sa+1,sizeof(sa));
for(int i=1; i<=n; i++) cnt[rk1[id[i]]]++;
for(int i=1; i<=m; i++) cnt[i]+=cnt[i-1];
for(int i=n; i>=1; i--) sa[cnt[rk1[id[i]]]--]=id[i];
rerank();
x=0;
for(int i=1; i<=n; i++) {
if(rk2[sa[i]]==rk2[sa[i-1]]&&rk2[sa[i]+k]==rk2[sa[i-1]+k]) rk1[sa[i]]=x;
else rk1[sa[i]]=++x;
}
}
for(int i=1; i<=n; i++) printf("%d ",sa[i]);
return 0;
}
标签:cnt,后缀,id,int,数组,--,SA,sa
From: https://www.cnblogs.com/Diamondan/p/17052284.html