一个字符串 $s$,你要把它分成若干段,有两种合法的段
1.段长为 $1$,代价为 $a$
2.这个段是前面所有段拼起来组成的字符串的字串,代价为 $b$
问最小代价
$|s| leq 5000$
sol:
赛后看到带 log 的过了十分不解...
考虑 dp
$f_i = min(f_{i-1} + a,space min{f_j + b})$ ($s[i+1,j]$ 要在 $s[1,i]$ 中作为子串出现)
子串的话写个 SAM 就可以线性求了,具体就是 extend 一个前缀,然后看后面能匹配几位
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch; for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } int n, a, b; int root, last, dfn; int tr[25010][26], fa[25010], mxlen[25010]; char s[5010]; int trans[5010][5010], f[5010]; void extend(int x) { int p = last, np = last = ++dfn; mxlen[np] = mxlen[p] + 1; for(; !tr[p][x]; p = fa[p]) tr[p][x] = np; if(!p) fa[np] = root; else { int q = tr[p][x]; if(mxlen[p] == mxlen[q] + 1) fa[np] = q; else { int nq = ++dfn; fa[nq] = fa[q]; memcpy(tr[nq], tr[q], sizeof(tr[nq])); fa[q] = fa[np] = nq; for(; tr[p][x] == q; p = fa[p]) tr[p][x] = nq; } } } int main() { root = last = ++dfn; n = read(), a = read(), b = read(); scanf("%s", s + 1); rep(i, 1 ,n) { extend(s[i] - 'a'); int now = root; rep(j, i+1, n) { if(tr[now][s[j] - 'a']) trans[j][i] = 1, now = tr[now][s[j] - 'a']; else break; } } rep(i, 1, n) { f[i] = f[i - 1] + a; dwn(j, i-1, 1) if(trans[i][j]) f[i] = min(f[i], f[j] + b); } cout << f[n] << endl; }