UVA 1630 folding

本文介绍了一种利用动态规划思想实现的字符串压缩算法,该算法能够通过查找并标记重复字符串组来达到压缩目的。通过实例详细解释了算法的具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:折叠一个字符串,使得其成为一个尽量短的字符串  例如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))


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值