高一還不會SA就退役吧
例題
UOJ#35. 後綴排序
這是一道模板題。
讀入一個長度爲 的由小寫英文字母組成的字符串,請把這個字符串的所有非空後綴按字典序從小到大排序,然後按順序輸出後綴的第一個字符在原串中的位置。位置編號爲 到 。
除此之外爲了進一步證明你確實有給後綴排序的超能力,請另外輸出 個整數分別表示排序後相鄰後綴的最長公共前綴的長度。
輸入格式
一行一個長度爲 的僅包含小寫英文字母的字符串。
輸出格式
第一行 個整數,第 個整數表示排名爲 的後綴的第一個字符在原串中的位置。
第二行 個整數,第 個整數表示排名爲 和排名爲 的後綴的最長公共前綴的長度。
樣例一
input
ababa
output
5 3 1 4 2
1 3 0 2
explanation
排序後結果爲:
a
aba
ababa
ba
baba
限制與約定
時間限制:
空間限制:
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