[BZOJ1090][SCOI2003]字符串折叠
[BZOJ1090][SCOI2003]字符串折叠
试题描述
折叠的定义如下:
-
一个字符串可以看成它自身的折叠。记作 \(S \Rightarrow S\)。
-
\(X(S)\) 是 \(X(X>1)\) 个 \(S\) 连接在一起的串的折叠。记作 \(X(S) \Rightarrow SSSS…S(X个S)\)。
-
如果 \(A \Rightarrow A’, B \Rightarrow B’\),则 \(AB \Rightarrow A’B’\) 例如,因为 \(3(A) \Rightarrow AAA, 2(B) \Rightarrow BB\),所以 \(3(A)C2(B) \Rightarrow AAACBB\),而 \(2(3(A)C)2(B) \Rightarrow AAACAAACBB\)
给一个字符串,求它的最短折叠。例如 \(AAAAAAAAAABABABCCD\) 的最短折叠为:\(9(A)3(AB)CCD\)。
输入
仅一行,即字符串 \(S\),长度保证不超过 \(100\)。
输出
仅一行,即最短的折叠长度。
输入示例
NEERCYESYESYESNEERCYESYESYES
输出示例
14
数据规模及约定
见“输入”
题解
设 \(f(l, r)\) 表示对于区间 \([l, r]\) 内的字符串最短能压缩成的长度。那么两种转移:第一种,拼接,即 \(f(l, r) = min\{f(l, k) + f(k + 1, r) | k \in [l, r)\}\);第二种,对于周期串,枚举折叠几倍。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
#define maxn 110
int n, f[maxn][maxn], fa[maxn];
char str[maxn];
int cntdig(int x) {
int cnt = 0;
while(x) cnt++, x /= 10;
return cnt;
}
int main() {
scanf("%s", str + 1);
n = strlen(str + 1);
rep(i, 1, n) rep(j, i, n) f[i][j] = j - i + 1;
rep(len, 1, n) rep(l, 1, n - len + 1) {
int r = l + len - 1;
rep(i, l, r - 1) f[l][r] = min(f[l][r], f[l][i] + f[i+1][r]);
fa[1] = fa[2] = 1;
rep(i, 2, r - l + 1) {
int j = fa[i];
while(j > 1 && str[i+l-1] != str[j+l-1]) j = fa[j];
fa[i+1] = str[i+l-1] == str[j+l-1] ? j + 1 : 1;
}
if(fa[r-l+2] < (r - l + 1 >> 1) + 1) continue;
// printf("[%d, %d]\n", l, r);
// rep(i, 1, r - l + 2) printf("%d%s", fa[i], i < r - l + 2 ? "->" : "\n");
int tlen = r - l + 2 - fa[r-l+2], ini = tlen;
for(; ini < r - l + 1; ini += tlen) if((r - l + 1) % ini == 0)
f[l][r] = min(f[l][r], f[r-ini+1][r] + cntdig((r - l + 1) / ini) + 2);
// printf("f[%d][%d] = %d\n", l, r, f[l][r]);
}
printf("%d\n", f[1][n]);
return 0;
}
总感觉在哪做过这道题。