高一还不会SA就退役吧
例题
UOJ#35. 后缀排序
这是一道模板题。
读入一个长度为 n n n 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 1 1 1 到 n n n。
除此之外为了进一步证明你确实有给后缀排序的超能力,请另外输出 n − 1 n - 1 n−1 个整数分别表示排序后相邻后缀的最长公共前缀的长度。
输入格式
一行一个长度为
n
n
n 的仅包含小写英文字母的字符串。
输出格式
第一行
n
n
n 个整数,第
i
i
i 个整数表示排名为
i
i
i 的后缀的第一个字符在原串中的位置。
第二行 n − 1 n - 1 n−1 个整数,第 i i i 个整数表示排名为 i i i 和排名为 i + 1 i + 1 i+1 的后缀的最长公共前缀的长度。
样例一
input
ababa
output
5 3 1 4 2
1 3 0 2
explanation
排序后结果为:
a
aba
ababa
ba
baba
限制与约定
1
≤
n
≤
1
0
5
1 \leq n \leq 10^5
1≤n≤105
时间限制:
1
s
1\texttt{s}
1s
空间限制:
256
MB
256\texttt{MB}
256MB
SA
一个卵用并不大的东西
SA能做的SAM基本都能做,除了O(1)求后缀的LCP
但有些题就是要这么搞也没办法
一些定义:
st:字符串
rank[i]:st[i…n]的字典序排名(显然各不相同)
sa[i]:排名为i的首字母出现位置(rank的逆数组),即sa[rank[i]]=i
hi[i](即height):st[sa[i-1]…n]和st[sa[i]…n]的LCP长度,hi[i]=h[sa[i]]
h[i]:st[sa[rank[i]-1]…n]和st[i…n]的LCP长度,h[i]=hi[rank[i]]
h[i]即表示以i为结尾的后缀和i排名的上一位的LCP长度
rank
首先是rank的求法
暴力肯定布星,考虑倍增求rank
每次把相邻的长度为2k段两段的rank,用二维桶排来求出新的rank(可重,但最终一定不重)
具体:先排个位,再排十位,因为邻接表的性质,所以要反着提
height
直接求height不方便,所以引入了h数组
h数组有一个非常显然且重要的性质:
h[i]≥h[i-1]-1
证明:
显然h[i]至少为h[i-1]删掉i-1,即至少为h[i-1]-1
简单又自然
有了这个性质后就可以线性求出h数组,然后可以求出height
(有可能sa[rank[i]-1]>i,所以两个都不能超出边界)
height的性质
可以利用height的性质来搞事
st[sa[i]…n]与st[sa[j]…n]的LCP=min(height[i+1…j])
证明:
设st[sa[i]…n]与st[sa[j]…n]的LCP长度为x,则x≥min(height[i+1…j])
若x>min(height[i+1…j]),则与字典序连续相违背,所以x≤min(height[i+1…j])
综上,x=min(height[i+1…j])
对应到原串中,st[i…n]和st[j…n]的LCP长度为min(height[rank[i]+1…rank[j]])(rank[i]<rank[j])
然而这题并没有用到这个性质
以后再填坑
code
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
int n,i,j,k,l,len;
int A[100001];
int pre[100001];
int Ls[100001];
int st[100001];
int h[100001]; //h[i]=hi[rank[i]]
int hi[100001]; //the LCP of sa[i-1] and sa[i] hi[i]=h[sa[i]]
int rank[200001];
int sa[200001];
int Rank[200001];
int Bz[26];
char ch;
int main()
{
// freopen("a.in","r",stdin);
// freopen("b.out","w",stdout);
// freopen("UOJ35.in","r",stdin);
ch=getchar();
while (ch>='a' && ch<='z')
{
st[++n]=ch-'a';
ch=getchar();
Bz[st[n]]=1;
}
fo(i,0,25)
Bz[i]+=Bz[i-1];
fo(i,1,n)
rank[i]=Bz[st[i]];
k=1;
while (k<=n)
{
fo(i,1,n)
{
pre[i]=Ls[rank[i+k]];
Ls[rank[i+k]]=i;
}
l=n;
fd(i,n,0)
{
while (Ls[i])
{
A[l--]=Ls[i];
Ls[i]=pre[Ls[i]];
}
}
fo(i,1,n)
{
pre[A[i]]=Ls[rank[A[i]]];
Ls[rank[A[i]]]=A[i];
}
l=n;
fd(i,n,0)
{
while (Ls[i])
{
A[l--]=Ls[i];
Ls[i]=pre[Ls[i]];
}
}
j=0;
fo(i,1,n)
Rank[i]=rank[i];
fo(i,1,n)
{
if (i==1 || Rank[A[i]]!=Rank[A[i-1]] || Rank[A[i]+k]!=Rank[A[i-1]+k])
++j;
rank[A[i]]=j;
}
k+=k;
}
fo(i,1,n)
sa[rank[i]]=i;
fo(i,1,n)
if (rank[i]>1)
{
h[i]=max(h[i-1]-1,0);
while (i+h[i]-1<n && sa[rank[i]-1]+h[i]-1<n && st[i+h[i]]==st[sa[rank[i]-1]+h[i]])
++h[i];
if (h[i] && st[i+h[i]-1]!=st[sa[rank[i]-1]+h[i]-1])
--h[i];
}
fo(i,2,n)
hi[i]=h[sa[i]];
fo(i,1,n)
printf("%d ",sa[i]);
printf("\n");
fo(i,2,n)
printf("%d ",hi[i]);
printf("\n");
}
参考资料
https://www.cnblogs.com/heyujun/p/10300582.html
https://www.cnblogs.com/cjyyb/p/8335194.html
https://blog.csdn.net/cold_chair/article/details/62909232