题目大意:折叠一个字符串,使得其成为一个尽量短的字符串 例如AAAAAA变成6(A)
而且这个折叠是可以嵌套的,例如 NEEEEERYESYESYESNEEEEERYESYESYES 会变成 2(N5(E)R3(YES))
思路:首先是要找相同的字符串组,然后进行标记。就着上面的例子是, 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;这种的。在这时候对于这个字符串,一定要取最短的结合方式(最优子结构)
然后进行串与串之间的结合 接下来详情见代码吧。
注意DP时要分两种情况,一种是 这个范围是由多个相同子字符串构成的,要单独列出来进行比较。
另外一种是 不是由多个相同子字符串构成的,这时候进行循环比较,求出最优解。
#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
using namespace std;
#define bracket 2
//折叠字符串,可以嵌套折叠
//如果使用递归的做法,就是不断地进行拆分问题,但是这个问题有最优子结构
//所以dp方程式就如下 dp[a][b]=min(dp[a][b],dp[a][k]+dp[k+1][b]) 答案就是dp[0][n-1]
//题目步骤如下:1,先按照长度拆分字符串,然后对折叠前后进行比较,更新答案,并且记录下来
int dp[110][110]={0};
string cmp[110][110];
int save[110][110]={0};
string input;
int lg(int number)
{
int result=0;
do
{
number=number/10;
result++;
}while(number!=0);
return result;
}
stack<string> stk;
int main()
{
while(cin>>input)
{
memset(dp,0,sizeof(dp));
memset(save,0,sizeof(save));
//列举字符串长度
int length=input.size();
for(int i=0;i<length;i++)
{
for(int j=0;j<length;j++)
{
if(j>=i)
{
dp[i][j]=j-i+1;
cmp[i][j].clear();
for(int k=i;k<=j;k++)
{
cmp[i][j].push_back(input[k]);
}
}
}
}
for(int len=1;len<=length;len++)
{
for(int start=0;start<len;start++)
{
int left=start;
int right=start+len-1;
int samestart=start;
string temp=cmp[left][right];
stk.push(temp);
left=right+1;
right=left+len-1;
while(right<length)
{
temp=cmp[left][right];
if(temp==stk.top())
{
stk.push(temp);
int maxsize=stk.size();
for(int i=samestart;i<=right;i=i+len)//别忘记顺带更新子字符串的子字符串
{
int cp=bracket+lg(maxsize)+dp[samestart][samestart+len-1];
if(cp<dp[i][right])//取最好的结合方式
{
save[i][right]=maxsize;
dp[i][right]=cp;
}
maxsize--;
}
}
else
{
samestart=left;
while(stk.empty()==false) stk.pop();
stk.push(temp);
}
left=right+1;
right=left+len-1;
}
while(stk.empty()==false) stk.pop();
}
}
for(int len=1;len<=length;len++)
{
for(int i=0;;i++)
{
int j=i+len-1;
if(j>=length) break;
if(save[i][j]!=0)//单独列出
{
int number=lg(save[i][j]);
//字符是 [i][i+((j-i)+1)/save[i][j]]
if(number+bracket+dp[i][i+len/save[i][j]-1]<=dp[i][j])
{
dp[i][j]=number+bracket+dp[i][i+len/save[i][j]-1];
string &t=cmp[i][j];
t.clear();
int aux=save[i][j];while(aux!=0){ t.push_back(aux%10+'0');aux=aux/10; }
reverse(t.begin(),t.end());
t=t+"("+cmp[i][i+len/save[i][j]-1]+")";
}
}
for(int k=i;k<j;k++)
{
if(dp[i][j]>dp[i][k]+dp[k+1][j])
{
dp[i][j]=dp[i][k]+dp[k+1][j];
cmp[i][j]=cmp[i][k]+cmp[k+1][j];
}
}
}
}//分情况,一种是属于重复区间的(ABABABAB),一种是独立区间的(abcccccd)
cout<<cmp[0][length-1]<<endl;
}
return 0;
}
如果大家实在不知道错在哪 ,我推荐以下这组数据
NEEEEEEEEEEEEERYESYESYESYESEEEEEERYESYESYESYES
答案是
N7(E)2(6(E)R4(YES))