后缀数组学习记录

(文章更新中)

作为一个小菜鸟,也开始学后缀数组了。

关于模板哪些事

后缀数组第一步:后缀排序—求sa和rk

在后缀数组中,有一环避不开的就是后缀排序了,后缀排序中,我们使用的是基数排序(不懂的可以手动跳转以下–>基数排序(百度百科))这里运用的也是基数排序,这里先推荐一道后缀排序的板子:P3809后缀排序
同时,还需要倍增,把复杂度优化到nlogn。
其实还有一种O(n)的算法,但代码复杂度太高,所以使用不多。
关于后缀排序的板子和各种讲解,网上也很多,这里就不单独拉出来了,就简单讲一下自己的体会:

  • 在后缀排序以及后面的后缀数组中,由于涉及的桶和for循环比较多,所以一定要注意细节,小心不要打错,如果数组名比较相似,改都很难改好。
  • 对于其中的m,初始值一定要设好,不要太吝啬了,之前一道题目就因为初始值太小,导致后面的一个数组爆了,RE只有40分。

代码如下:

void qsort() {
	for(int i=0; i<=m; ++i)
		c[i]=0;
	for(int i=1; i<=n; ++i)
		++c[x[i]];
	for(int i=1; i<=m; ++i)
		c[i]+=c[i-1];
	for(int i=n; i>=1; --i)
		sa[c[x[y[i]]]--]=y[i];
}
void get_sa() {
	for(int i=1; i<=n; ++i)
		x[i]=s[i],y[i]=i;
	qsort();
	for(int k=1,num; num!=n; k<<=1,m=num) {
		num=0;
		for(int i=n-k+1; i<=n; ++i)
			y[++num]=i;
		for(int i=1; i<=n; ++i)
			if(sa[i]>k)
				y[++num]=sa[i]-k;
		qsort();
		swap(x,y),num=0;
		for(int i=1; i<=n; ++i)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
	}
	for(int i=1; i<=n; ++i)
		rk[sa[i]]=i;
}

后缀数组第二步—求height数组

在后缀数组中,height数组就是在排好序之后相邻的两个字符串的lcp。
求法:

  • O(n2) 暴力枚举
    暴力枚举想必大家都会,暴力匹配,不过这样的效率太低,速度很慢。
  • O(nlogn)二分求lcp
    于是考虑优化,由于lcp本身可以用哈希+二分优化到logn的复杂度,所以这里自然可以优化成nlogn。
  • O(n)运用一个神奇的性质求
    height数组还有一个神奇的性质:height[rk[i]]>=height[rk[i-1]]-1。运用这个性质,我们就可以线性求出height数组了

下面就是一波模板:

int n,m,sa[N],h[N],rk[N],s[N],a[N],x[N],y[N],c[N];

struct Sa {
	void qsort() {
		for(int i=0; i<=m; ++i)
			c[i]=0;
		for(int i=1; i<=n; ++i)
			++c[x[i]];
		for(int i=1; i<=m; ++i)
			c[i]+=c[i-1];
		for(int i=n; i>=1; --i) 
			sa[c[x[y[i]]]--]=y[i];
	}
	void get_sa() {
		for(int i=1; i<=n; ++i)
			x[i]=s[i],y[i]=i;
		qsort();
		for(int k=1,num; num!=n; k<<=1,m=num) {
			num=0;
			for(int i=n-k+1; i<=n; ++i)
				y[++num]=i;
			for(int i=1; i<=n; ++i)
				if(sa[i]>k)
					y[++num]=sa[i]-k;
			qsort();
			swap(x,y),num=0;
			for(int i=1; i<=n; ++i)
				x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
		}
		for(int i=1; i<=n; ++i)
			rk[sa[i]]=i;
	}
	void get_head() {
		int k=0,j;
		for(int i=1; i<=n; ++i) {
			if(rk[i]==1) continue;
			if(k) --k;
			j=sa[rk[i]-1];
			while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
				++k;
			h[rk[i]]=k;
		}
	}
} SA;

猜你喜欢

转载自blog.csdn.net/weixin_43464026/article/details/87301263