/*
translation:
将一串字符串折叠成一串尽量短的字符串
solution:
记忆化搜索dp
可以看出一串字符串有3种情况:
1.字符串本身就已经是最简短
2.可以折叠成某一个更短的字符串
3.只有一部分能够折叠成更短的字符串,这时需要分成两部分来求解,需要遍历来确定拆分的位置
根据以上3种情况即可编码
note:
* 字符串折叠类型的题可以考虑区间dp和记忆化搜索
# check方法里面的第二重循环的范围错误了,应该是i+len<=e,而不是i+len-1<=e
date:
2017.1.15
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100 + 5;
const int INF = 1e8;
int dp[maxn][maxn]; //dp[a][b]代表字符串[a,b]区间上折叠后的最小长度是多少
string ans[maxn][maxn], str; //ans[a][b]代表区间[a,b]的答案
int check(int s, int e)
{
for(int len = 1; len <= (e-s+1)/2; len++){ //遍历长度
if((e - s + 1) % len) continue;
bool flag = true;
for(int i = s; i + len <= e; i++){
if(str[i] != str[i+len]){
flag = false;
break;
}
}
if(flag) return len;
}
return 0;
}
int solve(int s, int e)
{
if(dp[s][e] != -1) return dp[s][e];
if(s == e){
ans[s][e] = str[s];
return dp[s][e] = 1;
}
int len = INF, pos; //分成左右两部分求解
for(int m = s; m < e; m++){
int temp = solve(s, m) + solve(m+1, e);
if(temp < len){
len = temp;
pos = m; //找到拆分成2部分的位置
}
}
ans[s][e] = ans[s][pos] + ans[pos+1][e];
int l = check(s, e); //按照折叠方式来求解
if(l){
char num[20];
sprintf(num, "%d", (e - s + 1) / l);
string t = num + string("(") + ans[s][s+l-1] + string(")");
if(t.length() < len){
ans[s][e] = t;
len = t.length();
}
}
return dp[s][e] = len;
}
int main()
{
//freopen("in.txt", "r", stdin);
while(cin >> str){
int len = str.length();
memset(dp, -1, sizeof(dp));
solve(0, len-1);
cout << ans[0][len-1] << endl;
}
return 0;
}
uva1630(dp记忆化搜索)
最新推荐文章于 2021-08-08 15:04:55 发布