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;
}
原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ1090.html