【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;
}