Technocup 2019 C. Compress String

 

一个字符串 $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;
}
View Code

 

posted @ 2019-03-04 15:23  探险家Mr.H  阅读(252)  评论(0编辑  收藏  举报