SA(后缀数组)——倍增法模板

倍增法原理:

一步一步把n个后缀按照它们的前 2 ^ k 的字符排序。
当我们求出按前 2 ^ k 个字符排序的顺序时,对于按照前 2 ^ (k + 1) 个字符的排序,可以把它拆成前后两个 2 ^ k 个字符部分, 由于它们的顺序我们是知道的,于是打一个二维桶排即可。

Height_i 表示排名第 i 位的后缀和排名第 i - 1 位的后缀的最长公共前缀。
Height性质: Height_rank[j] >= Height_rank[j - 1] - 1
P.S:可以利用这个性质O(n)求Height,证明显然。

更新:2019.4.29
下面这个板应该是没有锅的了,经过了时间的考验。

有锅请私我。

struct SA {
	int n, m, s[N], rk[N], sa[N], tp[N], tx[N], he[N], f[19][N];
	void rs() {
		fo(i, 1, m) tx[i] = 0;
		fo(i, 1, n) tx[rk[tp[i]]] ++;
		fo(i, 1, m) tx[i] += tx[i - 1];
		fd(i, n, 1) sa[tx[rk[tp[i]]] --] = tp[i];
	}
	int cmp(int *f, int x, int y, int z) { return f[x] == f[y] && f[x + z] == f[y + z];}
	void build() {
		s[0] = s[n + 1] = -1; tp[n + 1] = 0; //*这一行的清空在多组数据中很重要
		fo(i, 1, n) rk[i] = s[i], tp[i] = i;
		m = n; rs();
		for(int p = 1, w = 1; p < n; m = p, w *= 2) {
			p = 0; fo(i, n - w + 1, n) tp[++ p] = i;
			fo(i, 1, n) if(sa[i] > w) tp[++ p] = sa[i] - w;
			rs(); fo(i, 1, n) tp[i] = rk[i];
			rk[sa[1]] = p = 1;
			fo(j, 2, n) rk[sa[j]] = cmp(tp, sa[j - 1], sa[j], w) ? p : ++ p;
		}
		int k = 0, j;
		for(int i = 1; i <= n; he[rk[i ++]] = k)
			for(k ? k -- : 0, j = sa[rk[i] - 1]; s[i + k] == s[j + k]; k ++);
		fo(i, 1, n) f[0][i] = he[i];
		fo(j, 1, 18) fo(i, 1, n) {
			f[j][i] = f[j - 1][i];
			if(i + a2[j - 1] <= n) f[j][i] = min(f[j][i], f[j - 1][i + a2[j - 1]]);
		}
	}
	int ask(int x, int y) {
		x = rk[x], y = rk[y];
		if(x > y) swap(x, y);
		x ++; int l = lg2[y - x + 1];
		return min(f[l][x], f[l][y - a2[l] + 1]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值