后缀排序,教你AC紫题

话不多说放
后缀排序是在求后缀数组 sa。
我们还需要一个 rk 数组计入排名,最后就可以用 rk 求出 sa 了。
假设有一个串wzhtql(顺带%一下)。
步骤如下,第一步将每个字符排序,结果如下。
w z h t q l
5 6 1 4 3 2
\begin{bmatrix} w& z &h &t &q&l \\ 5&6 &1 &4 &3 & 2 \end{bmatrix}
可以看到没有相同的了,可以结束了,但是我们把流程走完。
接下来倍增,每个数和后面第一个数接起来,如果后面每这个数用 0 代替。
\begin{bmatrix} w& z &h &t &q&l \\ 56&61 &14 &43 &32 & 20 \end{bmatrix}
然后将这些数字排序。
这样接下来就是后面第二个不断倍增。
这个例子可能不太明显,换了例子:wzhwzh。
先排序。
\begin{bmatrix} w& z &h &w &z&h \\ 3&1 &5 &3 &1 & 5 \end{bmatrix}
然后:
\begin{bmatrix} w& z &h &w &z&h \\ 31&15 &53 &31 &15 & 50 \end{bmatrix}
再排序,不断倍增:
\begin{bmatrix} w& z &h &w &z&h \\ 3&1 &6 &3 &1 & 5\\ 35&13 &61 &35 &10 & 50\\ 3&2 &6 &3 &1 & 5\\ 31&25 &60 &30 &10 & 50\\ 4&2 &6 &3 &1 & 5 \end{bmatrix}
所以最终的 rk 数组就求出来了,没看懂的可以多看几遍,正确性请自行证明,没啥难的。
那怎么排序呢:
直接快排是 O(n\log^{2}n)
也可以用基数排序,比快排快点: O(nk\log n)k 大概是 13 ,快排的一个 \log 是20,差了一半不到。
最快的是用计数排序是 O(n\log n) 的不过常数有 8 左右,比快排快了一倍。
编码应该挺简单的,给一个求 rk 数组的过程,怎么从 rk 转化成 sa 请自行思考。

void calc_sa()
{
	a[0].x=-1;
	sort(a+1,a+1+n,cmp);
	int sum=-1;
	for(int i=1;i<=n;++i)
	if(a[i].x==a[i-1].x) rk[a[i].id]=sum;
	else
	{
		sum++;
		rk[a[i].id]=sum;
	}
	for(int k=1;k<=n;k*=2)
	{
		for(int i=1;i<=n;++i)
		{
			if(i+k>n) a[i].x=1ll*rk[i]*N;
			else a[i].x=1ll*rk[i]*N+rk[i+k];
			a[i].id=i;
		}
		sort(a+1,a+1+n,cmp);
		sum=-1;
		for(int i=1;i<=n;++i)
		if(a[i].x==a[i-1].x) rk[a[i].id]=sum;
		else
		{
			sum++;
			rk[a[i].id]=sum;
		}
	}
}

我用的是快排有点卡常,想要更快可以用基数和计数两个排序。

2 个赞

有多卡常自己看

%%%%%%%%%%%%%%%%%%%。

想起了之前lzy大佬的博客

大佬你的 \LaTeX 炸了

1 个赞

标点符号在洛谷是不空的啊。
感觉论坛可以两边不用空格也行

还有这里

1 个赞