标签:
链接 : http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1620
题意 : 给一个字符串 问怎么压缩字符串使得最终个数最小 具体怎么压缩请参照图示 很好明白。
题目就是需要找到 对于每个后缀 看成一个新字符串 找出它的前缀的最小循环节。
过程和大白书 P213页 是一样的,只需要对每个后缀跑一遍KMP求出周期。
剩下的过程就是区间DP了。
dp[i][j] = min ( dp[i][k], dp[k+1][j] );
如果i - j这一段存在循环节,那么dp[i][j]还可以使用压缩规则进行再一次的更新。最终dp[1][n]就是答案。
#pragma comment(linker, "/STACK:10240000,10240000") #include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdlib> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <set> #include <map> #define mod 4294967296 #define MAX 0x3f3f3f3f #define lson o<<1, l, m #define rson o<<1|1, m+1, r #define SZ(x) ((int)ans.size()) #define MAKE make_pair #define INFL 0x3f3f3f3f3f3f3f3fLL #define mem(a) memset(a, 0, sizeof(a)) const double pi = acos(-1.0); const double eps = 1e-9; const int N = 503; const int M = 20005; typedef long long ll; using namespace std; int f[N], g[N][N], dp[N][N], v[N][N]; char s[N]; void KMP(char *s, int r) { f[0] = f[1] = 0; for(int i = 1; s[i]; i++) { int j = f[i]; while(j && s[j] != s[i]) j = f[j]; f[i+1] = (s[i] == s[j] ? j + 1 : 0); } int n = strlen(s); for(int i = 2; i <= n; i++) { if(f[i] > 0 && i % (i-f[i]) == 0) { g[r+1][i+r] = i/(i-f[i]); } } } int main() { //freopen("in.txt","r",stdin); int ca = 1; while(scanf("%s", s) && s[0] != '0') { int n = strlen(s); memset(g, -1, sizeof(g)); for(int i = 0; i < n; i++) { KMP(s+i, i); } printf("Case %d: ", ca++); for(int i = 1; i <= n; i++) { for(int j = i; j <= n; j++) { //printf("--g[%d][%d] = %3d\n", i, j, g[i][j]); if(g[i][j] == -1) { v[i][j] = j-i+1; } else { int num = g[i][j]; int cnt = (j-i+1) / g[i][j]; while(num) { num /= 10; cnt++; } if(g[i][j] != j-i+1) cnt += 2; v[i][j] = min(j-i+1, cnt); } //printf("++g[%d][%d] = %3d, \n", i, j, v[i][j]); } } for(int len = 1; len <= n; len++) { for(int i = 1; i <= n-len+1; i++) { int j = i + len - 1; dp[i][j] = v[i][j]; for(int k = i; k < j; k++) { dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]); } if(g[i][j] != -1) { int num = (j-i+1) / g[i][j], tmp = 0; int p = i+num-1; if(num != 1) tmp += 2; int y = g[i][j]; while(y) { y /= 10; tmp++; } dp[i][j] = min(dp[i][j], tmp + dp[i][p]); } } } printf("%d\n", dp[1][n]); } return 0; }
CSU 1620: A Cure for the Common Code (区间DP KMP预处理)
标签:
原文地址:http://blog.csdn.net/u013923947/article/details/45803771