[SCOI2003]字符串折叠
区间 \(DP\) , 设 \(f[i][j]\) 表示区间 \((i, j)\) 的最短折叠长度, 然后枚举 \(k \in (i, j)\) , 判断 \((i, k)\) 是否是 \((i, j)\) 的一个循环节, 是就取 \(f[i][j] = \min (f[i][j], f[i][k] + 2 + num[(j - i + 1) / (k - i + 1)])\) , \(num[i]\) 表示 \(i\) 这个数字的字符长度. 然后每次取 \(f[i][j] = \min (f[i][j], f[i][k] + f[k + 1][j])\) .
\(code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
char s[N];
int n, f[N][N], num[N];
bool Check(int l, int r, int R) {
int len = r - l + 1, Len = R - l + 1;
if (Len % len) return false;
for (int i = l; i <= R; i++) {
int j = l + (i - l) % len;
if (s[i] != s[j]) return false;
}
return true;
}
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; i++) num[i] = num[i / 10] + 1;
for (int i = 1; i <= n; i++) f[i][i] = 1;
for (int l = 2; l <= n; l++) {
for (int i = 1; i + l - 1 <= n; i++) {
int j = i + l - 1;
f[i][j] = l;
for (int k = i; k < j; k++) {
if (Check(i, k, j)) f[i][j] = min(f[i][j], f[i][k] + 2 + num[l / (k - i + 1)]);
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
}
}
}
printf("%d", f[1][n]);
return 0;
}