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))
Source
题解
容易看出来是区间dp...
转移方程$f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]$
但是怎么处理字符串的折叠这一块很懵逼
首先一个字符串能够折叠成它的一个字串,要满足的一个很显然的条件是能够被这个子串整除,那么就可以减少掉很多无用状态的枚举了
然后对于能够整除的情况,一个一个向后暴力匹配,时间复杂度还是过得去的毕竟n那么小
注意在匹配的过程中如果有一位不相同就可以直接跳掉了
转移方程$f[l][r]=min(f[l][r],f[l][l+k-1]+2+idx((r-l+1)/k)$
$idx$是用来判断前面的数字的长度的,如果小于10返回1小于100返回2小于1000返回3
初始化就是$f[l][r]=r-l+1$但是这里会没有覆盖到单个字母的情况,所以还要再初始化一下$f[i][i]=1$
#includeusing namespace std;char ch[200];int n;int f[200][200];int idx(int x){ if(x<10)return 1; if(x<100)return 2; return 3;}bool check(int l,int r,int len){ for(int i=1;i<=len;i++){ for(int j=l+i-1;j+len<=r;j+=len){ if(ch[j]!=ch[j+len])return 0; } } return 1;}int main(){ scanf("%s",ch+1); n=strlen(ch+1); for(int l=n;l;l--){ f[l][l]=1; for(int r=l+1;r<=n;r++){ f[l][r]=r-l+1; for(int k=l;k<=r;k++){ f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]); } for(int k=1;k<=r-l+1;k++){ if((r-l+1)%k==0&&check(l,r,k)){ f[l][r]=min(f[l][r],f[l][l+k-1]+idx((r-l+1)/k)+2); } } } } printf("%d\n",f[1][n]); return 0;}