Build a String

题目

https://www.hackerrank.com/challenges/build-a-string/problem

题意

有两种对字符串的操作:

  • 在末尾加一个字符,代价为 \(A\)
  • 在末尾加一个子串,代价为 \(B\)

求构建字符串 \(S\) 的最小代价。

题解

  • dp 是肯定的,问题是需要求(2 选 1):
    • 1: 每一个前缀中最长的后缀,使得该前缀去掉该后缀形成的前缀中包含这个后缀。(向前收集)
    • 2: 每一个后缀中最长的前缀,使得整个串去掉该后缀形成的前缀中包含这个前缀。(向后更新)
  • 选 1。设结束位置为 \(e\) 前缀的状态为 \(u\)。沿 \(fa\) 指针向上爬到状态 \(v\) 使得 \(len_{min}(v)+endpos_{min}(v)<=e\),此时满足条件的最大长度为 \(min\{len_{max}(v), e - endpos_{min}(v)\}\)
  • 某个状态的 \(endpos_{min}\) 就是子树上关键点对应的最小的 \(endpos\),通过 dfs 预处理,顺便预处理倍增。
  • 复杂度 \(O(n\log n)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
#ifdef zerol
#define dbg(args...) do { cout << "\033[32;1m" << #args << " -> "; err(args); } while (0)
#else
#define dbg(...)
#endif
void err() { cout << "\033[39;0m" << endl; }
template<template<typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args) { for (auto x: a) cout << x << ' '; err(args...); }
template<typename T, typename... Args>
void err(T a, Args... args) { cout << a << ' '; err(args...); }
// -----------------------------------------------------------------------------
const int maxn = 3E4 * 2 + 100;

namespace sam {
const int M = maxn;
int t[M][26], fa[M], len[M] = {-1}, sz, last, rpos[maxn], pos[M];
void init() { memset(t, 0, sizeof t); memset(pos, -1, sizeof pos); last = 1; sz = 2; }
void ins(int ch, int pp) {
int p = last, np = last = sz++;
len[np] = len[p] + 1; rpos[pp] = np; pos[np] = pp;
for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np;
if (!p) { fa[np] = 1; return; }
int q = t[p][ch];
if (len[p] + 1 == len[q]) fa[np] = q;
else {
int nq = sz++; len[nq] = len[p] + 1;
memcpy(t[nq], t[q], sizeof t[0]);
fa[nq] = fa[q];
fa[np] = fa[q] = nq;
for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq;
}
}
vector<int> G[M];
const int SP = 20;
int mp[M], pa[M][SP];
void build() { FOR (i, 1, sz) G[i].clear(); FOR (i, 2, sz) G[fa[i]].push_back(i); }
int dfs(int u, int fa) {
int ret = pos[u] == -1 ? M : pos[u];
pa[u][0] = fa;
FOR (i, 1, SP) pa[u][i] = pa[pa[u][i - 1]][i - 1];
for (int& v: G[u])
ret = min(ret, dfs(v, u));
return mp[u] = ret;
}
}

int go(int p) {
#define fa(x) pa[x][0]
using namespace sam;
int u = rpos[p];
FORD (i, SP - 1, -1)
if (len[fa(pa[u][i])] + 1 + mp[pa[u][i]] > p) u = pa[u][i];
u = fa(u);
return min(len[u], p - mp[u]);
}

int f[maxn];
char s[maxn];
int n, A, B;
int main() {
int T; cin >> T;
while (T--) {
cin >> n >> A >> B;
scanf("%s", s);
sam::init();
FOR (i, 0, n) sam::ins(s[i] - 'a', i);
sam::build(); sam::dfs(1, 1);
FOR (i, 1, n + 1) {
f[i] = f[i - 1] + A;
f[i] = min(f[i], f[i - go(i - 1)] + B);
}
cout << f[n] << endl;
}
}