题目
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;
}