[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]
", l, r);
// rep(i, 1, r - l + 2) printf("%d%s", fa[i], i < r - l + 2 ? "->" : "
");
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
", l, r, f[l][r]);
}
printf("%d
", f[1][n]);
return 0;
}
总感觉在哪做过这道题。