【DP 训练】Folding, ACM/ICPC NEERC 2002, UVa1630

来源:互联网 发布:手机称重软件 编辑:程序博客网 时间:2024/05/19 09:01

基于dp的字符串压缩

思路:首先是要找相同的字符串组,然后进行标记。就着上面的例子是, NEEEEERYESYESYESNEEEEERYESYESYES,变成 2个 NEEEEERYESYESYES,然后里面再变成N5(E)R3(YES),这么一看,似乎就是把这个大的字符串拆成小的字符串进行结合,类似于分治的思想。而且这个题目有最优子结构,对于每个可以合并的串,一定要使用最短的结合方式,否则整个结果一定不是最短的。

于是DP的方程就得到了 dp[ i ][ j ]=min(dp[ i ][ j ],dp[ i ][ k ]+dp[ k+1 ][ j ]) dp[ i ][ j ] 从i到j代表 从i到j 的子字符串长度


首先用一个string的二位数组,把从i到j的最小字符串存下来,然后首先遍历字符串,找出所有长度一样的字符串组,用一个二维数组save进行标记。

例如

NEEEEEEERYESYESYESNEEEEEEERYESYESYES

我们发现(下标从1开始) 2~8位是长度为1的字符串组  所以标记save[2][8]=7; (7是字字符串个数)

别忘了更新类似于save[3][8]=6;这种的。在这时候对于这个字符串,一定要取最短的结合方式(最优子结构)



#include<bits/stdc++.h>using namespace std;string str;int dp[110][110];string fold[110][110];int len;const int INF = 1e9;int zip(int L,int R)//压缩函数 输入保证了L<R {//@return: 循环节长度 for(int i=1;i<=(R-L+1)/2;i++)//枚举循环长度 {if((R-L+1)%i)continue; bool flag = true;for(int j=L;j+i<=R;j++){if(str[j]!=str[j+i]){flag = false;break;}}if(flag)return i;}return 0;}int sol(int L,int R){if(dp[L][R]!=-1)return dp[L][R];if(L==R){dp[L][R] = 1;fold[L][R] = str[L];return 1;}int ret = INF,k;for(int i=L;i<R;i++){int tmp = sol(L,i)+sol(i+1,R);if(tmp<ret){k = i;ret = tmp;}}fold[L][R] = fold[L][k]+fold[k+1][R];ret = sol(L,k)+sol(k+1,R);int tlen = zip(L,R);//对重复串进行压缩 if(tlen){bool flag = true;for(int i=L;i<=R;i++)if(str[i]=='('||str[i]==')')flag = false;char t[10];sprintf(t,"%d",(R-L+1)/tlen);string nstr = t+string("(")+fold[L][L+tlen-1]+string(")");if(flag&&nstr.size()<ret){ret = nstr.size();fold[L][R] = nstr;}}dp[L][R] = ret;return ret;}int main(void){ios::sync_with_stdio(false);while(cin>>str){memset(dp,-1,sizeof(dp));len = str.size();sol(0,len-1);cout<<fold[0][len-1]<<endl;}return 0;}


1 0
原创粉丝点击