1. 程式人生 > 實用技巧 >芝士:字尾陣列(SA)

芝士:字尾陣列(SA)

例子

傳送門

基數排序

它的工作原理是將待排序的元素拆分為k個關鍵字(比較兩個元素時,先比較第一關鍵字,如果相同再比較第二關鍵字……),然後先對第k關鍵字進行穩定排序,再對第k-1關鍵字進行穩定排序,再對第k-2關鍵字進行穩定排序……最後對第一關鍵字進行穩定排序,這樣就完成了對整個待排序序列的穩定排序。

​ --OI WIKI

過程

pace 1

考慮對兩個字串進行比較

定義其為\(s1,s2\)

如果\([0,i]\)已經能比較出大小,那麼就不需要考慮\([i+1,lens)\)的關係了

反之,如果\([0,i]\)不能比較出大小,那麼就需要考慮\([i+1,lens)\)

的關係

pace 2

定義位置\(i\)的字串為\([i,lens)\)的字元

比如\(abaaba\)

\[0:abaaba\\1:baaba\\2:aaba\\3:aba\\4:ba\\5:a\\ \]

考慮對於所有位置\(i\)字串按第一位進行排序

那麼排完序就會有

\[abaaba\\aaba\\aba\\a\\baaba\\ba \]

現在單獨考慮第二位的大小,考慮到字尾之間的聯絡,現在真的需要知道每一個字尾具體是什麼樣子麼?

位置\(0\)的字串的第二位不就是位置\(1\)的字串的第一位?

那麼對於每一個位置都可以找到一個第一位與之第二位相對應

那麼我們就可以知道只考慮第二位的字尾之間的大小關係

然後我們知道第二位的大小關係,又知道第一位的大小關係、

那麼運用基數排序的思想,就可以知道前兩位,即\([0,1]\)的大小關係

pace 3

按照pace2的內容進行遞推,

如果我們知道只考慮前k位的大小關係,即\([0,k-1]\)的大小關係

現在我們想求出只考慮\([0,2k-1]\)的大小關係

考慮對於位置\(i\)的字串,其\([k,2k-1]\)的字元就是位置\(i+k\)的前\(k\)

故可以知道只考慮\([k,2k-1]\)的大小關係

那麼按照基數排序,就可以有\([0,2k-1]\)的大小關係

程式碼

#include<iostream>
#include<cstring>
using namespace std;
char s[1000005];
int n;
int m;
int sa[1000005];
int rank1[1000005];
int rank2[1000005];
int t_rank[1000005];
int solve_hash(char c)
{
	if('0'<=c&&c<='9')
		return c-'0'+1;
	if('A'<=c&&c<='Z')
		return c-'A'+11;
	return c-'a'+37;
}
void init()
{
	for(int i=1;i<=n;i++)
	{
		rank1[i]=solve_hash(s[i]);
		rank2[i]=i;
	}
}
void _sort()
{
	for(int i=1;i<=m;i++)
		t_rank[i]=0;
	for(int i=1;i<=n;i++)
		t_rank[rank1[i]]++;
	for(int i=1;i<=m;i++)
		t_rank[i]+=t_rank[i-1];
	for(int i=n;i>=1;i--)
	{
		sa[t_rank[rank1[rank2[i]]]]=rank2[i];
		t_rank[rank1[rank2[i]]]--;
	}
}
void get_sa()
{
	for(int w=1,p=0;p<n&&w<=n;m=p,p=0,w<<=1)
	{
		for(int i=n-w+1;i<=n;i++)
			rank2[++p]=i;
		for(int i=1;i<=n;i++)
		{
			if(sa[i]>w)
			{
				rank2[++p]=sa[i]-w;
			}
		}
		_sort();
		swap(rank1,rank2);
		p=rank1[sa[1]]=1;
		for(int i=2;i<=n;i++)
			if(rank2[sa[i]]==rank2[sa[i-1]]&&rank2[sa[i]+w]==rank2[sa[i-1]+w])
				rank1[sa[i]]=p;
			else
				rank1[sa[i]]=++p;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>(s+1);
	n=strlen((s+1));
	m=62;
	init();
	_sort();
	get_sa();
	for(int i=1;i<=n;i++)
		cout<<sa[i]<<' ';
	return 0;
}

字尾的LCT

咕咕咕