319. 折叠序列

题目
319. 折叠序列
比尔正在试图用折叠重复子序列的方式紧凑的表示由大写字母’A’到’Z’组成的字符序列。

例如,表示序列AAAAAAAAAABABABCCD的一种方式是10(A)2(BA)B2(C)D。

他通过以下方式定义了折叠的字符序列以及它们的展开变换:

1、包含带个字符的序列被认为是折叠序列,展开它得到的序列为它本身。

2、如果S和Q是两个折叠序列,并且S可以展开得到S’,Q可以展开得到Q’,则认为SQ也是一个折叠序列,并且SQ展开得到S’Q’。

3、如果S是折叠序列,则X(S)也是折叠序列,其中X为大于1的整数。如果S展开得到S’,则X(S)展开得到X个S’。

根据定义可以展开任意给出的折叠序列,现在给出原序列,请你将它折叠,并使得折叠序列包含尽可能少的字符数。

输入格式
输入包含一行由大写字母构成的字符序列,序列长度在1到100之间。

输出格式
输出包含字符数最少的折叠序列,如果答案不唯一则任意输出一个即可。

输入样例:
AAAAAAAAAABABABCCD
输出样例:
9(A)3(AB)CCD

这可能是我做过的最难的区间DP题了吧。
算法 区间DP
既然是DP就必定要考虑状态转移。
有如下两种转移方式
1 由区间DP的模板想出的路径,即利用小区间的折叠串来拼凑出大区间的折叠串
2 有题目的意思想出的路径,即由几个相同子串构成的子串折叠。
如此一来,这个题目就只有实现上的问题了。

#include<bits/stdc++.h>
using namespace std;
const int ll=200;
string f[ll][ll];
char s[200],v[20]={"0123456789"};
int n;
int find(int l,int r){
	int len=r-l+1;
	for(int i=1;i<=len;i++){
		if(len%i!=0) continue;
		int a=l,b=l+i-1,flag=1;
		while(b<=r){
			if(f[l][l+i-1]!=f[a][b]){
				flag=0;
				break;
			}
			a=b+1;			
			b=a+i-1;
		}		
		if(flag){
			return i;
		}
	}
	return 0;	
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)
			for(int k=i;k <= j;k++)			
				f[i][j] += s[k];
	
	for(int len = 2;len <= n;len ++)
		for(int l = 1;l <= n-len+1;l ++){
			int r = l+len-1;
			for(int k = l;k < r;k ++){
				if(f[l][k].size()+f[k+1][r].size()<f[l][r].size()){
					f[l][r]=f[l][k];
					f[l][r]+=f[k+1][r];
				}
			}
			int get=find(l,r);			
			string cun;
			if(get){
				int num=(r-l+1)/get;
				while(num){
					int zr=num;
					while(zr>=10)
						zr=(zr-zr%10)/10;
					cun+=v[zr];
					if(num-10*zr>=0){
					    num-=10*zr;
					    if(num==0) cun+=v[0];
					}
					else num-=zr;
				}
				cun+="(";			
				cun+=f[l][l+get-1];
				cun+=")";
			}
			if(cun.size()&&cun.size()<f[l][r].size())
				f[l][r]=cun;
		}
	cout<<f[1][n];	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值