字符串折叠
题目描述
折叠的定义如下:
- 一个字符串可以看成它自身的折叠。记作S = S
- X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
-
如果A = A’, B = B’,则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。
输入输出格式
输入格式:
仅一行,即字符串S,长度保证不超过100。
输出格式:
仅一行,即最短的折叠长度。
输入输出样例
说明
一个最短的折叠为:2(NEERC3(YES))
分析:
康战士的考试里面出的题目。考的时候直接弃疗。。。(先膜一波老余AK%%%)
需要用区间DP来做,首先定义动规数组f[l][r],表示从l到r这一段区间内的字符串折叠后能得到的最短结果。那么枚举折叠的区间,然后枚举左右区间,再枚举可折叠的长度,也就是枚举区间长度的所有因数,然后进行判断该区间是否可以折叠,如果可以则进行状态转移。值得注意的是,转移完以后还需要在进行依次断点枚举,表示将该区间分成两次折叠,看能否得到最短折叠。当然,蒟蒻不擅长动规,还是听了老余讲课,又参考了大佬的博客才弄懂的。如果上面的思路不太懂,就直接看代码吧,代码好懂多了。
Code:
1 //It is made by HolseLee on 28th May 2018 2 //Luogu.org P4302 3 #include<bits/stdc++.h> 4 using namespace std; 5 char s[107];int f[107][107]; 6 inline bool check(int l,int r,int k) 7 { 8 for(int i=l+k,p=0;i<=r;i++,p=(p+1)%k) 9 if(s[i]!=s[l+p])return false; 10 return true; 11 } 12 inline int get(int x) 13 {int ret=2;while(x)x/=10,ret++;return ret;} 14 int main() 15 { 16 freopen("char.in","r",stdin); 17 freopen("char.out","w",stdout); 18 memset(f,0x7f,sizeof(f)); 19 scanf("%s",s+1);int n=strlen(s+1); 20 for(int i=1;i<=n;i++)f[i][i]=1; 21 for(int i=2;i<=n;i++) 22 for(int l=1;l+i-1<=n;l++){ 23 int r=l+i-1; 24 for(int k=1;k*k<=i;k++){ 25 if(i%k==0){ 26 if(check(l,r,i/k))f[l][r]=min(f[l][r],f[l][l+i/k-1]+get(k)); 27 if(check(l,r,k))f[l][r]=min(f[l][r],f[l][l+k-1]+get(i/k));}} 28 for(int k=l;k<=r;k++) 29 f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);} 30 printf("%d",f[1][n]);return 0; 31 }