洛谷 UVA11022 String Factoring
思路
区间 dp。设 \(f_{l,r}\) 为子串 \([l,r]\) 压缩的最短长度,显然有 \(f_{l,r} \gets \min\limits_{i=l}^{r-1} f_{l,i} + f_{i+1,r}\)。还要考虑压缩 \([l,r]\) 的情况。
设 \(fail_{i,j}\) 为后缀 \([i,n]\) 的失配数组,跑 \(n\) 遍 KMP 即可求得。找循环节就暴力跳 \(fail\)。设当前区间为 \([l,r]\),\(fail_{l,r} = k\),当 \(r - l + 1 - k\ |\ r - l + 1\),最长循环节的长度就为 \(r - l + 1 - k\)。这样做时间复杂度为 \(O(n^3)\)。由于 \(n \le 80\),所以也可以暴力判断循环节,复杂度 \(O(n^4)\)。
代码
code
/*
p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int maxn = 90;
int n, f[maxn][maxn], fail[maxn][maxn];
char s[maxn];
void solve() {
n = strlen(s + 1);
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; ++i) {
f[i][i] = 1;
}
memset(fail, 0, sizeof(fail));
for (int k = 1; k <= n; ++k) {
for (int i = k + 1, j = 0; i <= n; ++i) {
while (j && s[i] != s[j + k]) {
j = fail[k][j];
}
if (s[i] == s[j + k]) {
++j;
}
fail[k][i] = j;
}
}
for (int p = 2; p <= n; ++p) {
for (int i = 1, j = p; j <= n; ++i, ++j) {
for (int k = i; k < j; ++k) {
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
}
int x = fail[i][j];
while (x) {
if (p % (p - x) == 0) {
f[i][j] = min(f[i][j], f[i][i + p - x - 1]);
}
x = fail[i][x];
}
}
}
printf("%d\n", f[1][n]);
}
int main() {
// int T = 1;
// scanf("%d", &T);
while (scanf("%s", s + 1) == 1 && s[1] != '*') {
solve();
}
return 0;
}