[SCOI2003]字符串折叠 (区间DP)
题目描述
折叠的定义如下:
- 一个字符串可以看成它自身的折叠。记作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))
Solution
这道题属于很典型的区间DP.
状态定义:
f[ i ][ j ] 表示从i 到 j 的最小长度.
前导状态:
f [ i ][ j ] 初始化为原长 j - i +1.
f [ i ][ k ] 和 f[ k+1 ][ j ] 枚举断点并且更新.
如果要实现合并操作的话.
我们枚举的两个区间要满足几个条件:
1. 后面 的区间长度要整除前面合并的第一项长度 (因为合并都是从前面开始所以只需考虑后区间被前区间合并)
2. 后面的区间每一段都与其相同.
对于这个,我们直接暴力即可实现.
然后在 hzwer 学长那里学了一个高级的枚举. 无需担心区间边界问题 ! 递归实现 !
代码
#include<bits/stdc++.h> using namespace std; char ch[101]; int f[101][101]; bool vich[101][101]; bool judge(int l,int r,int cl,int cr) { if((r-l+1)%(cr-cl+1)!=0) return 0; for(int i=l;i<=r;i++) if(ch[i]!=ch[(i-l)%(cr-cl+1)+cl]) return 0; return 1; } int cal(int x) {int ans=0; while(x%10!=0){ans++;x/=10;}return ans;} //cal 为合并后数字的长度 int ans(int l,int r) { if(vich[l][r])return f[l][r]; vich[l][r]=1; f[l][r]=r-l+1; //初始化为其长度. for(int i=l;i<r;i++) { f[l][r]=min(f[l][r],ans(l,i)+ans(i+1,r)); if(judge(i+1,r,l,i)) f[l][r]=min(f[l][r],ans(l,i)+2+cal((r-i)/(i-l+1)+1)); } return f[l][r]; } int main() { scanf("%s",ch); cout<<ans(0,strlen(ch)-1)<<endl; }