题面
解法
我还是太菜了……3277总时限10s,然而T了……
- 首先显然是把这些串拼起来中间用分隔符隔开,然后构造出后缀数组。
- 然后我们枚举子串的开头部分。可以发现,最大能贡献到答案中的长度是满足单调性的,所以我们可以考虑二分这个长度,假设为 l e n len len。
- 那么我们就可以求出这个后缀所在的最长的区间使得这个区间中后缀两两的 l c p lcp lcp不小于 l e n len len,假设是区间 [ l , r ] [l,r] [l,r]。然后我们只要看区间 [ l , r ] [l,r] [l,r]中是否有 k k k个不同的数就可以了。
- 对于如何求解 [ l , r ] [l,r] [l,r]中是否有 k k k个不同的数,因为 k k k是一直不变的,所以我们可以对于每一个区间的起点 l l l求出最小的 r r r使得 [ l , r ] [l,r] [l,r]中有至少 k k k种数。这个可以直接扫一遍求出。
- 时间复杂度: O ( n log 2 n ) O(n\log^2 n) O(nlog2n)
- 应该有复杂度更优的做法,但是可能要后缀自动机了……我太菜了并不会……
代码
#include <bits/stdc++.h>
#define inf 1 << 30
#define N 200010
using namespace std;
int n, m, sum, c[N], s[N], y[N], sa[N], ans[N], col[N], nxt[N], lim[N], rnk[N], f[N][21];
int lcp(int i, int j) {
int l = i + 1, r = j, t = log2(r - l + 1);
return min(f[l][t], f[r - (1 << t) + 1][t]);
}
void Sort() {
for (int i = 1; i <= m; i++) s[i] = 0;
for (int i = 1; i <= n; i++) s[rnk[i]]++;
for (int i = 1; i <= m; i++) s[i] += s[i - 1];
for (int i = n; i; i--) sa[s[rnk[y[i]]]--] = y[i];
}
void build(string st) {
n = st.size() - 1, m = 200;
for (int i = 1; i <= n; i++) rnk[i] = st[i], y[i] = i;
Sort(); int len = 0;
for (int k = 1; k <= n; k <<= 1, m = len, len = 0) {
for (int i = n - k + 1; i <= n; i++) y[++len] = i;
for (int i = 1; i <= n; i++) if (sa[i] > k) y[++len] = sa[i] - k;
Sort(), swap(rnk, y), rnk[sa[1]] = len = 1;
for (int i = 2; i <= n; i++)
rnk[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? len : ++len;
}
for (int i = 1, k = 0; i <= n; i++) {
if (rnk[i] == 1) continue;
if (k) k--; int j = sa[rnk[i] - 1];
while (st[i + k] == st[j + k]) k++;
f[rnk[i]][0] = k;
}
for (int j = 1; (1 << j) <= n; 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]);
}
void calc(int k) {
int r = 0, tot = 0;
for (int i = 1; i <= n; i++) {
while (tot < k && r < n) {
s[col[++r]]++;
if (s[col[r]] == 1) tot++;
}
if (tot < k) lim[i] = inf; else lim[i] = r;
s[col[i]]--; if (s[col[i]] == 0) tot--;
}
}
bool check(int x, int len) {
int l = 1, r = x - 1, tl = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (lcp(mid, x) >= len) tl = mid, r = mid - 1;
else l = mid + 1;
}
l = x + 1, r = n; int tr = x;
while (l <= r) {
int mid = (l + r) >> 1;
if (lcp(x, mid) >= len) tr = mid, l = mid + 1;
else r = mid - 1;
}
return lim[tl] <= tr;
}
int main() {
ios::sync_with_stdio(false);
string st = "$"; int tot, k; cin >> tot >> k;
for (int i = 1; i <= tot; i++) {
string str; cin >> str; st = st + str;
if (i < tot) st = st + '#';
}
build(st); int cnt = tot, las = n + 1;
for (int i = n; i; i--)
if (st[i] == '#') cnt--, las = i; else nxt[rnk[i]] = las, col[rnk[i]] = cnt;
memset(s, 0, sizeof(s)); calc(k);
for (int i = 1; i <= n; i++) {
if (st[sa[i]] == '#') continue;
int l = 1, r = nxt[i] - sa[i], ret = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(i, mid)) ret = mid, l = mid + 1;
else r = mid - 1;
}
ans[col[i]] += ret;
}
for (int i = 1; i <= tot; i++) cout << ans[i] << ' '; cout << "\n";
return 0;
}