bzoj1090 [SCOI2003]字符串折叠
1090: [SCOI2003]字符串折叠
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1718 Solved: 1146
[Submit][Status][Discuss]
Description
折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) SSSS…S(X个S)。 3. 如果A A’, BB’,则AB A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) AAACBB,而2(3(A)C)2(B)AAACAAACBB 给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
Input
仅一行,即字符串S,长度保证不超过100。
Output
仅一行,即最短的折叠长度。
Sample Input
NEERCYESYESYESNEERCYESYESYES
Sample Output
14
HINT
一个最短的折叠为:2(NEERC3(YES))
分析:一开始觉得和bzoj1068差不多.用那道题的状态去推状态转移方程发现不是那么容易推.因为这里括号必须要是匹配的,而那一题并不要求R,M匹配.
实际上这道题还要简单一些,区间dp的基本状态表示方法:f[l][r]就能够转移.
两种方式:1.f[l][r] = min{f[i][k] + f[k+1][r]}. 2.f[l][r] = min{f[l][k] + 2 + (r - l + 1) / (k - l + 1)的结果的位数}.这两个转移分别是将子区间合并和将字符串折叠.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; char s[110]; int n,f[110][110]; int cal(int x) { if (x >= 1 && x <= 9) return 1; if (x >= 10 && x <= 99) return 2; if (x >= 100) return 3; } bool check(int l,int mid,int r) { int x = l; for (int i = mid + 1; i <= r; i++) { if (s[x] != s[i]) return false; x++; if (x == mid + 1) x = l; } return true; } int main() { scanf("%s",s + 1); n = strlen(s + 1); for (int l = 1; l <= n; l++) { for (int i = 1; i + l - 1 <= n; i++) { int j = i + l - 1; f[i][j] = j - i + 1; for (int k = i; k < j; k++) f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j]); for (int k = i; k < j; k++) { int len = k - i + 1; if(l % len != 0) continue; if (check(i,k,j)) f[i][j] = min(f[i][j],f[i][k] + 2 + cal(l / len)); } } } printf("%d\n",f[1][n]); return 0; }