后缀数组SA

后缀数组SA

模板

花了不少时间才理解倍增求 S A SA SA的实现方法,我还是太菜了。

定义 s a [ i ] sa[i] sa[i]表示排名为 i i i的后缀的起始位置。
定义 r a n k [ i ] rank[i] rank[i]表示起始位置为 i i i的后缀的排名。
显然两者之前互逆。

void solve()
{
	int m=122;
	for (int i=1;i<=m;i++) cnt[i]=0;
	for (int i=1;i<=n;i++) cnt[x[i]=a[i]]++;
	for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for (int i=n;i>=1;i--) sa[cnt[x[i]]--]=i;
	
	for (int k=1;k<=n;k<<=1)
	{
		int p=0;
		for (int i=1;i<=m;i++) y[i]=0;
		for (int i=n-k+1;i<=n;i++) y[++p]=i;
		for (int i=1;i<=n;i++) if (sa[i]>k) y[++p]=sa[i]-k;
		
		for (int i=0;i<=m;i++) cnt[i]=0;
		for (int i=1;i<=n;i++) cnt[x[y[i]]]++;
		for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for (int i=n;i>=1;i--) sa[cnt[x[y[i]]]--]=y[i];
		
		swap(x,y);
		x[sa[1]]=p=1;
		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])?p:++p;
		if (p>=n) break;
		m=p;
	}
}

Height以及LCP

定义 L C P ( x , y ) LCP(x,y) LCP(x,y)表示字符串 x x x y y y之间的最长公共前缀长度。

定义 h e i g h t [ i ] height[i] height[i]表示 s u f f i x [ s a [ i − 1 ] ] suffix[sa[i-1]] suffix[sa[i1]] s u f f i x [ s a [ i ] ] suffix[sa[i]] suffix[sa[i]] L C P LCP LCP的长度,即相邻排名的后缀的 L C P LCP LCP长度。

容易发现一个性质
对于任意的 j , k j,k j,k,若 r a n k [ j [ < r a n k [ k ] rank[j[<rank[k] rank[j[<rank[k],则 s u f f i x ( j ) , s u f f i x ( k ) suffix(j),suffix(k) suffix(j),suffix(k) L C P LCP LCP的长度为
m i n i = r a n k [ j ] + 1 r a n k [ k ] h e i g h t [ i ] min_{i=rank[j]+1}^{rank[k]}height[i] mini=rank[j]+1rank[k]height[i]
即两个后缀 j , k j,k j,k L C P LCP LCP长度是排名在它们之间所有的后缀(包括 s u f f i x ( k ) suffix(k) suffix(k))的 h e i g h t height height值的最小值。

根据这一性质,倘若我们可以求出 h e i g h t [ i ] height[i] height[i],我们就可以通过 S T ST ST表, O ( n l g n ) O(nlgn) O(nlgn)预处理, O ( 1 ) O(1) O(1)询问两个后缀的 L C P LCP LCP答案了。

而对于 h e i g h t height height数组,也有一个重要的性质:
h e i g h t [ i − 1 ] − 1 ≤ h e i g h t [ i ] height[i-1]-1\leq height[i] height[i1]1height[i]
于是可以 O ( n ) O(n) O(n)计算 h e i g h t height height了。

void get_height()
{
	for (int i=1;i<=n;i++) rnk[sa[i]]=i;
	for (int i=1,j=0;i<=n;i++)
	{
		if (j) j--;
		while (a[i+j]==a[sa[rnk[i]-1]+j]) j++;
		height[rnk[i]]=j;
	}
}

SA的简单应用

1.求最长重复子串

答案为 h e i g h t height height最大值。

2.求最长重复k次子串

二分答案,转化为判断是否存在长度为 x x x的子串重复至少 k k k次。将所有相邻且 h e i g h t height height大于等于 x x x的都分在一组,判断是否有一组的后缀个数大于 k k k即可。

时间复杂度 O ( n ) O(n) O(n)

3.本质不同子串个数

答案为产生的新串数量-重复出现的串的数量。
∑ i ( n − i + 1 ) − h e i g h t [ r n k [ i ] ] \sum_{i} (n-i+1)-height[rnk[i]] i(ni+1)height[rnk[i]]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值