1. 程式人生 > 其它 >「字串」字尾陣列

「字串」字尾陣列

字尾陣列

int x[N],y[N],c[N],sa[N],rk[N],h[N];
for(int i=1;i<=n;++i) ++c[x[i]=s[i]];
//c[i]是桶,x[i]是第i個元素的第一關鍵字排名
for(int i=2;i<=m;++i) c[i]+=c[i-1];
//求字首和,確定每個關鍵字最多在第幾名
for(int i=n;i>=1;--i) sa[c[x[i]]--]=i;
//確定排名為sa[i]的數在哪個位置,這裡正序和倒序迴圈都可以,為了和下面保持一致採用倒序
for(int k=1;k<=n;k<<=1)
{
	int num=0;//計數器
	for(int i=n-k+1;i<=n;++i) y[++num]=i;
	//y[i]存的是第二關鍵字排名為y[i]的字尾,第一關鍵字的位置
	//n-k+1到n沒有第二關鍵字,排名最靠前
	for(int i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k;
	//如果滿足排名為sa[i]的字尾在第k位之後,那麼它可以做別人的第二關鍵字,它的第一關鍵字在k位之前
	//i列舉的是第二關鍵字的排名,第二關鍵字靠前先入隊
	for(int i=1;i<=m;++i) c[i]=0;//清空
	for(int i=1;i<=n;++i) ++c[x[i]];
	for(int i=2;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;
	//y的順序是按第二關鍵字排序的,x[y[i]]是第二關鍵字排名為y[i]的元素的第一關鍵字排名
	//倒序滿足y[i]排名越靠後,在sa中排名越靠後
	swap(x,y);
	//將舊的x存在y中
	x[sa[1]]=1,num=1;
	//更新x[i]
	for(int i=2;i<=n;++i)
		x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
	//如果二者第一二關鍵字都相同則排名相同
	if(num==n) break;//已經排出了n名則字尾排序完成
	m=num;
    for(int i=1;i<=n;++i) rk[sa[i]]=i;
	int k=0;
	for(int i=1;i<=n;++i)
	{
		if(rk[i]==1) continue;//第一位的height為0
		if(k) --k;
		int j=sa[rk[i]-1];
        while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;
        h[rk[i]]=k;
	}
}

P4051 [JSOI2007]字元加密

加倍成環,然後做字尾陣列,只取\(sa[i]\leq n\)的部分

\(s[sa[i]-1]\)拼起來

P2870 [USACO07DEC]Best Cow Line G

列舉左右端點,哪個小放哪個

如果出現相同的情況就要判斷後綴和字首的字典序了

假設\(S=AABCAA\)

\(S\)後新增一個間隔符,再翻轉加倍

\(S=AABCAA\#AACBAA\)

跑字尾陣列,字元相同時比較字尾排名大小

P2852 [USACO06DEC]Milk Patterns G

二分和並查集都可以,這裡選並查集

\(h[i]\)陣列從大到小將\(i,i-1\)集合合併,直到出現集合大於等於\(k\)

時,答案為\(h[i]\)