题目大意:给一串长度为$n$的数字,求出其中最长的至少出现$k(2\leqslant k\leqslant n)$次的字段,输出长度
题解:$SAM$后$DP$
卡点:多处数组未开两倍
C++ Code:
#include <cstdio>
#include <map>
#define maxn 20010
int n, k, ans;
inline int max(int a, int b) {return a > b ? a : b;}
namespace SAM {
int head[maxn << 1], cnt;
struct Edge {
int to, nxt;
} e[maxn << 1];
void add(int a, int b) {
e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
}
int root, idx, last, p, t, np;
int R[maxn << 1], fail[maxn << 1];
int sz[maxn << 1];
std::map<int, int> nxt[maxn << 1];
void init() {
fail[last = idx = root = 0] = -1;
}
void insert(int x) {
int now = ++idx;
R[now] = R[p = last] + 1;
sz[last = now] = 1;
for (; ~p && !nxt[p][x]; p = fail[p]) nxt[p][x] = now;
if (!~p) {fail[now] = root; return ;}
if (R[t = nxt[p][x]] == R[p] + 1) {fail[now] = t; return ;}
R[np = ++idx] = R[p] + 1;
nxt[np] = nxt[t];
fail[np] = fail[t]; fail[t] = fail[now] = np;
for (; ~p && nxt[p][x] == t; p = fail[p]) nxt[p][x] = np;
}
void dfs(int rt) {
for (int i = head[rt]; i; i = e[i].nxt) {
int v = e[i].to;
dfs(v);
sz[rt] += sz[v];
}
if (sz[rt] >= k) ans = max(ans, R[rt]);
}
}
int main() {
scanf("%d%d", &n, &k);
SAM::init();
for (int i = 1, x; i <= n; i++) scanf("%d", &x),
SAM::insert(x);
for (int i = 1; i <= SAM::idx; i++) SAM::add(SAM::fail[i], i);
SAM::dfs(0);
printf("%d\n", ans);
return 0;
}