后缀数组

对于概念去看白书或者论文吧(《后缀数组——处理字符串的有力工具》by:罗穗骞)

这里放上模板,一些解释在注释里。

#include <cstdio>
#include <cstring>
#define for1(i,a,n) for(i=a;i<=(n);++i)
#define for2(i,a,n) for(i=a;i<(n);++i)
#define for3(i,a,n) for(i=a;i>=(n);--i)
#define for4(i,a,n) for(i=a;i>(n);--i)
#define CC(i,a) memset(i, a, sizeof(i))
#define min(a,b) ((a)<(b)?(a):(b))

using namespace std;

const int N=1000000;
char s[N];
int sa[N], t1[N], t2[N], c[N], r[N], height[N], rank[N];

void hz(int* r, int n, int m) {
	int i, j, p, *x=t1, *y=t2, *t;
	for2(i, 0, m) c[i]=0;
	for2(i, 0, n) c[ x[i]=r[i] ]++;	//给排名为x[i]的计数
	for2(i, 1, m) c[i]+=c[i-1];		//将计数数组c累加,目的是给sa提供下标
	for3(i, n-1, 0) sa[ --c[x[i]] ]=i;	//sa数组用c提供的名次下标更新原数组下标
	for(j=1, p=0; j<=n	&& p<n; j<<=1, m=p) { //p<n时退出,因为此时排名已经不同,此优化很重要,并且每一次将m更新为最大值p
		p=0;
		for2(i, n-j, n) y[p++]=i;	//y数组充当第二关键字的sa数组,这里是初始化那些没有第二关键字的,即第二关键字为0的,所以他们最小放在前面
		for2(i, 0, n) if(sa[i]>=j) y[p++]=sa[i]-j;	//因为是第二关键字,所以要标到第一关键字的下标来
		for2(i, 0, m) c[i]=0;	//同之前的基数排序一样
		for2(i, 0, n) c[ x[y[i]] ]++;	//用第二关键字的下标引导rank,找到对应第一关键字的rank累计
		for2(i, 1, m) c[i]+=c[i-1];		//同上
		for3(i, n-1, 0) sa[ --c[x[y[i]]] ]=y[i];	//sa数组用c提供的 第二关键字尽量大中的 第一关键字下标 的名次更新到这个这个 第二关键字尽量大中的 第一关键字下标
		for(t=x, x=y, y=t, p=1, x[sa[0]]=0, i=1; i<n; ++i) //y数组可以不要了,将它赋值为这一次的rank,x数组作为下一个rank数组,所以更新
			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]?p-1:p++;	//当第一关键字的rank相等并且第二关键字的rank相等说明这个下标的rank和其中一个相等,所以名次还是为上一个
	}
}

void getheight(int n) {
	int i, j, k;
	for1(i, 1, n) rank[sa[i]]=i;	//因为sa下标从1开始,rank下标从0开始,值域从1开始
	for(k=i=0; i<n; height[rank[i++]]=k)	//根据h[i]>=h[i-1]-1,我们循环的是h[i],所以h[i-1]不需保存,其中h[i]我们用k来代替了
		for(k?k--:0, j=sa[rank[i]-1]; s[i+k]==s[j+k]; k++);		//因为h[i-1]-1是.............(自己看论文或白书去)。我们只需要根据前一个后缀来更新最大前缀。
}

int main() {
	scanf("%s", s);
	int i, n=strlen(s);
	for2(i, 0, n) r[i]=s[i]-'a'+1; //这里的r随便,如果知道范围的话,c数组可以相应的开小一些,我这里直接27了。。
	hz(r, n+1, 27); //为了让sa的下标从1开始,我们插入了一个比所有字符还要小的字符到末尾,所以sa的下标就从1开始了,方便计算height而不会导致越界~
	//...
	getheight(n);
	//...
	return 0;
}

例题:

  1. 【BZOJ】1692 & 1640: [Usaco2007 Dec]队列变换(后缀数组+贪心)
  2. 【BZOJ】1031: [JSOI2007]字符加密Cipher(后缀数组)
原文地址:https://www.cnblogs.com/iwtwiioi/p/3864075.html