标签:
链接 : 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