【Luogu P2852】[USACO06DEC]Milk Patterns G

链接:

洛谷

题目大意:

给出一个数字串找到一个最大的子串使得其出现次数大于等于 \(k\)

思路:

简单题。对后缀排序后,求 \(\mathrm{height}\) 数组。那么任意一段区间 \([l,r]\) 后缀的 \(\mathrm{height}\) 的最小值就是它们的 LCP,也是出现次数为 \(r-l+1\) 的最大子串。那么枚举左端点,长度为 \(k\),加 ST 表即可。

代码:

const int N = 2e4 + 10, M = 1e6 + 10;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, m, k;
int s[N], c[M];
int x[N], y[N], sa[N], height[N], Rank[N];

void SA() {
	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; 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; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
		
		swap(x, y);
		x[sa[1]] = 1, num = 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]? num: ++num);
		m = num;
		if (n == m) break;
	}
	return ;
}

void Height() {
	for (int i = 1; i <= n; i++) Rank[sa[i]] = i;
	int h = 0, j;
	for (int i = 1; i <= n; height[Rank[i++]] = h)
		for (h? h--: 0, j = sa[Rank[i] - 1]; s[i + h] == s[j + h]; h++);
	return;
}

int f[N][25], ans;
int QueryMin(int l, int r) {
	int t = log2(r - l + 1);
	return min(f[l][t], f[r - (1 << t) + 1][t]);
}

int main() {
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	n = Read(), k = Read() - 1;
	for (int i = 1; i <= n; i++) s[i] = Read(), m = max(m, s[i]);
	SA();
	Height();
	memset (f, 127 / 3, sizeof f);
	for (int i = 1; i <= n; i++) f[i][0] = height[i];
	for (int j = 1; j <= 23; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
	}
	for (int i = 1; i <= n - k + 1; i++)
		ans = max(QueryMin(i, i + k - 1), ans);
	printf ("%d\n", ans);
	return 0;
}
posted @ 2021-12-09 21:47  Jayun  阅读(32)  评论(0编辑  收藏  举报