BZOJ1090 [SCOI2003]字符串折叠 区间动态规划 字符串
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1090
题意概括
折叠的定义如下:
1. 一个字符串可以看成它自身的折叠。记作S
2. X(S)是X(X>1)个S连接在一起的串的折叠。
n<=100.让你求折叠之后的最小长度。
题解
(据说字符串的题有通用做法?——hash+乱搞??)
首先预处理出从第i个位置开始的连续j个字符最多重复了几次。
可以用哈希,但是数据范围小,直接暴力匹配就可以了。
然后,区间动归,记忆化dfs,dp[i][j]表示i~j这一段最少可以折叠成多少。
具体的状态转移见代码的dfs段。
代码
#include <cstring> #include <cstdlib> #include <algorithm> #include <cstdio> #include <cmath> using namespace std; const int N=100+5; const int Inf=10000; char str[N]; int n; int dp[N][N],match[N][N]; int num_digit(int x){ int ans=0; while (x) ans++,x/=10; return ans; } int dfs(int L,int R){ if (L>R) return 0; if (dp[L][R]!=-1) return dp[L][R]; dp[L][R]=dfs(L+1,R)+1; for (int i=1;L+i-1<=R;i++) for (int j=2;j<=match[L][i]&&L+i*j<=R+1;j++) dp[L][R]=min(dp[L][R],dfs(L,L+i-1)+num_digit(j)+2+dfs(L+i*j,R)); return dp[L][R]; } int main(){ scanf("%s",str+1); n=strlen(str+1); for (int i=1;i<=n;i++)//枚举位置 for (int j=1;i+j-1<=n;j++){//枚举长度 match[i][j]=0; for (int k=i+j;k<=n+1;k++){ if ((k-i)%j==0) match[i][j]++; if (str[k]!=str[k-j]) break; } } memset(dp,-1,sizeof dp); for (int i=1;i<=n;i++) dp[i][i]=1; printf("%d",dfs(1,n)); return 0; }