【LCP】后缀数组 + LCP + RMQ

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/giftedpanda/article/details/101788652

LCP(Longest Common Prefix):最长公共前缀

rank[i]:代表后缀i在 后缀数组中的下标 

height[i]:后缀i - 1 和后缀i 的最长公共前缀

对于两个后缀jk,不妨设rank[j] < rank[j] ,不难得出后缀j 和后缀 kLCP长度等于min(height[rank[j]+1] ,height[rank[j]+2],...,height[rank[k]])

LCP(j,k) = RMQ(height,rank[j]+1,rank[k])

按照定义计算height[i]需要O(n),整个height 数组需要O(n^2)。但是我们有O(n)的算法,我们用一个辅助数组h[i] = height[rank[i]],然后按照h[1],h[2],...,顺序递推计算。递推计算的基于一个性质:h[i] \geq h[i-1] - 1

设排在后缀i - 1前一个是后缀k。后缀k和后缀i - 1分别删除首字符之后得到后缀k + 1和后缀i ,因此后缀k+1一定排在后缀i前面,并且最长公共前缀为h[i-1] - 1

这个h[i-1]-1是一系列 h 值中的最小值,这些 h 值中包括后缀i和排在它前一个的后缀 pLCP长度,即h[i]。因此h[i] \geq h[i-1] - 1

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000000 + 7;
char s[maxn];
int sa[maxn], c[maxn], x[maxn], y[maxn], n, m;
int Rank[maxn], height[maxn];
// LCP rank[i] 后缀 i 的排名, height[i] = LCP(sa[i-1], sa[i])
int d[maxn][32];  // d[i][j] 以 i 开始长度为 2^j 的区间的最值
int suffixArray() // 后缀数组
{
	for(int i = 1; i <= m; i++) c[i] = 0;
	for(int i = 1; i <= n; i++) ++c[x[i] = s[i]];
	for(int i = 2; i <= m; i++) c[i] += c[i-1];
	for(int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
	for(int k = 1; k <= n; k <<= 1) {
		int 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;
		for(int i = 1; i <= m; i++) c[i] = 0;
		for(int i = 1; i <= n; i++) ++c[x[i]];
		for(int i = 2; i <= m; i++) c[i] += c[i-1];
		for(int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
		swap(x, y);
		num = 1, x[sa[1]] = 1;
		for(int i = 2; i <= n; i++) {
			x[sa[i]] = (y[sa[i-1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k]) ? num : ++num;
		}
		if(num >= n) break;
		m = num;
	}
	return 0;
}
void getHeight()
{
	int k = 0;
	// sa[i] 排名为 i 的后缀的下标
	// Rank[i] 后缀 i 的排名
	for(int i = 1; i <= n; i++) Rank[sa[i]] = i;
	for(int i = 1; i <= n; i++) {
		if(k) k--;
		int j = sa[Rank[i]-1];
		while(s[i+k] == s[j+k]) k++;
		height[Rank[i]] = k;
	}
}
void RMQ_init(int* a, int n)  // RMQ 初始化
{
	// height数组是从1开始的,而RMQ的数组是从0开始的
	for(int i = 0; i < n; i++) a[i] = a[i+1];
	for(int i = 0; i < n; i++) d[i][0] = a[i];
	for(int j = 1; (1 << j) <= n; j++) {
		for(int i = 0; i + (1 << j) - 1 < n; i++) {
			d[i][j] = min(d[i][j-1], d[i + (1 << (j-1))][j-1]);
		}
	}
}
int RMQ(int l, int r) // RMQ
{
	int k = 0;
	while((1 << (k + 1)) <= r - l + 1) k++;
	return min(d[l][k], d[r-(1<<k)+1][k]);
}
int main()
{
	while(scanf("%s", s + 1) == 1) {
		n = strlen(s + 1);
		m = 122;
		suffixArray();
		getHeight();
		RMQ_init(height, n);
		int l, r;
		scanf("%d %d", &l, &r);
		printf("%d\n", RMQ(l, r));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/giftedpanda/article/details/101788652