链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1090
Description
折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) SSSS…S(X个S)。 3. 如果A A’, BB’,则AB A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) AAACBB,而2(3(A)C)2(B)AAACAAACBB 给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
Input
仅一行,即字符串S,长度保证不超过100。
Output
仅一行,即最短的折叠长度。
Sample Input
NEERCYESYESYESNEERCYESYESYES
Sample Output
14
HINT
一个最短的折叠为:2(NEERC3(YES))
题解:这道题还是老规矩啦,设f[i][j]表示区间i~j能转换成的最短长度,然后再枚举一个k,分成两半,计算最小值,不过有些不同的是这次我们要判断区间i~j是否能被折叠为用i~k来表示,如果能的话,计算折叠后的长度是否短于之前的长度,如果短于,记录下来,我开始看到是字符串的时候还以为超级难,结果看懂后。。。。。
程序:
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<string> using namespace std; int tot,n,j,lent,f[200][200]; char s[200]; string s1; bool check(int x,int y,int z) { tot=(y-x+1)/(z-x+1);//增加的数字的长度 lent=0; for (int i=x;i<=y;i++) if (s[i]!=s[(i-x)%(z-x+1)+x]) return false;//这个我写的有点乱,其实就是判断是否能被折叠的,可以自己推一下,或者写的更清楚一点,我就不解释了, while (tot>0) { lent+=1; tot/=10; }//计算数字的长度 return true;//返回值 } int main() { cin>>s1; n=s1.size(); for (int i=1;i<=n;i++) s[i]=s1[i-1];//还是存到字符数组里方便些 for (int i=1;i<=n;i++) f[i][i]=1; for (int len=2;len<=n;len++) for (int i=1;i<=n-len+1;i++) { j=i+len-1; f[i][j]=j-i+1;//最开始的长度 for (int k=i;k<=j-1;k++) { if (len%(k-i+1)==0)//如果i~j的区间长度刚好能整除i~k的长度的话 if (check(i,j,k))//检查是否能被折叠 { f[i][j]=min(f[i][j],f[i][k]+lent+2);//lent是指那个增加的数字的长度,2是指括号 } f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);//不能被折叠的话,分成两半,来找最小值 } } cout<<f[1][n]<<endl;//输出答案 return 0; }