后缀数组

后缀排序

char s[N];
int n,sa[N],rk[N],ork[N<<1];
int buc[N],id[N],pid[N];

bool cmp(int a,int b,int w){return ork[a]==ork[b] && ork[a+w]==ork[b+w];}

void build()
{
	int m=(1<<17),p=0;
	for(int i=1; i<=n; i++) buc[rk[i]=s[i]]++;
	for(int i=1; i<=m; i++) buc[i]+=buc[i-1];
	for(int i=n; i>=1; i--) sa[buc[s[i]]--]=i;
	for(int w=1; ; w<<=1,m=p,p=0)
	{
		for(int i=n; i>n-w; i--) id[++p]=i;
		for(int i=1; i<=n; i++) if(sa[i]>w) id[++p]=sa[i]-w;
		for(int i=0; i<=m+1; i++) buc[i]=0;
		for(int i=1; i<=n; i++) buc[pid[i]=rk[id[i]]]++;
		for(int i=1; i<=m; i++) buc[i]+=buc[i-1];
		for(int i=n; i>=1; i--) sa[buc[pid[i]]--]=id[i];
		for(int i=1; i<=n; i++) ork[i]=rk[i];
		p=0;
		for(int i=1; i<=n; i++) rk[sa[i]]=cmp(sa[i-1],sa[i],w)? p:++p;
		if(p==n) break;
	}
}

\(h\) 数组

关键性质:\(h_{rk_i}\ge h_{rk_{i-1}}-1\)

int k=0;
for(int i=1; i<=tot; i++)
{
	if(k) k--;
	while(s[i+k]==s[sa[rk[i]-1]+k]) k++;
	h[rk[i]]=k;
}

应用

1、求任意两个后缀的 LCP

\[\operatorname{lcp}(i,j)=\min\limits_{p=\min(rk_i,rk_j)+1}^{\max(rk_i,rk_j)}h_p \]

2、求本质不同的子串个数

\[\dfrac{n(n+1)}{2}-\sum\limits_{i=2}^n h_i \]

3、与单调栈结合

\(h\) 数组倒过来可以得到一个柱状图,这是用单调栈解决问题的基础。以 P4248 [AHOI2013] 差异 为例,求两两后缀 \(\rm lcp\) 之和。

考虑按 \(rk\) 从小到大加入 \(h\)(其实就是依次加入 \(h_{1\sim n}\)),设 \(F(i)=\sum\limits_{p=1}^{i-1}|\operatorname{lcp}(sa_i,sa_p)| =\sum\limits_{p=1}^{i-1}\min\limits_{q=p+1}^i h_q\),答案即 \(\sum F(i)\)。用单调栈拆掉那个 \(\min\),变成维护单调栈矩形面积和。这是我们熟悉的问题。

一些题目

CF822E Liar

考虑朴素 DP,设 \(f_{i,j}\) 表示匹配到 \(s\) 的第 \(i\) 位,\(t\) 的第 \(j\) 位的最小次数,转移即考虑 \(s[i+1:]\)\(t[j+1:]\) 的最长公共前缀。考虑优化,注意到 \(x\leq 30\),所以值域定义域互换,设 \(f_{i,a}\) 表示用了 \(s\)\(i\) 位,\(a\) 个子串,最远能匹配到 \(t\) 的哪一位。转移不表。

P2178 [NOI2015] 品酒大会

因为是 \(r\) 相似也就一定是 \(0\sim r-1\) 相似,所以从大到小考虑。与 lcp 有关的是夹在这两个后缀排名间 \(h_i\)\(\min\),考虑并查集,每次将 \(h_i=r\) 的后缀 \(sa_i\)\(sa_{i-1}\) 合并,那么只需维护联通块大小,最大值、次大值、最小值、次小值即可。

P5341 [TJOI2019] 甲苯先生和大中锋的字符串

使用 SA 分析字符串的常见方式:将字符串按照 \(rk\) 排序。

我们在排序后的字符串取出一个长度为 \(k\) 的区间 \([i,i+k-1]\),显然这个区间的长度上界为 \(\operatorname{lcp}\limits_{j=i}^{i+k-1}s_j\)。下界就是 \(\max(h_{i+k},h_{i})\)。单调队列即可 \(\mathcal{O}(n)\)

posted @ 2024-08-25 15:34  xishanmeigao  阅读(2)  评论(0编辑  收藏  举报