bzoj 1090(区间dp)

本文介绍了一种利用区间动态规划解决字符串压缩问题的方法。通过枚举断点并判断子串是否为循环节来优化字符串个数,实现有效的压缩。整体算法时间复杂度为O(n^4)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:

给你一串字符串,你现在你可以把一串相同的串压缩成一个串,现在问你压缩之后最小的字符串个数。

分析:

一道非常有意思的区间dp的问题。

这个问题本质上跟石子合差不多,都是可以把区间某个部分压缩合并,本质上的状态转移方程均为:\(dp[l][r]=\min(dp[l][r],dp[l][k]+dp[k+1][r])\);而只不过在这个问题中,我们合并压缩的过程中计算贡献的方法跟石子合并不一样。

我们考虑在这个问题中如何求解贡献。现在要使得一个长串\(str_1\)能够用一个小串\(str_2\)表示出来,那么,显然这就代表着串\(str_2\)必定是\(str_1\)的某一个循环节,如果满足这样的条件,设循环节长度为\(len\),则当前的区间\([l,r]\)就可以对答案贡献出\(2+Bit(\frac{r-l+1}{len})\)的贡献。而这一步的更新,我们可以在枚举断点\(k\)的过程中进行更新,因此,如果枚举的区间\([l,k]\)是区间\([l,r]\)的循环节,则会有状态转移:\(dp[l][r]=\min(dp[l][r],dp[l][k]+2+Bit(\frac{r-l+1}{len})\)

处理好贡献之后,之后就是最基本的区间dp向上更新的过程。因为我们还需要判断某个子串是否是循环节,因此整体的时间复杂度为:\(\mathcal{O}(n^4)\)

代码:

#include <bits/stdc++.h>
#define maxn 105
using namespace std;
int dp[maxn][maxn];
char str[maxn];
const int inf=0x3f3f3f3f;
int getbit(int x){
    int cnt=0;
    while(x){
        cnt++;
        x/=10;
    }
    return cnt;
}
bool judge(int l,int r,int L,int R){
    int len=(R-L+1),k=R;
    while(1){
        for(int i=L;i<=R;i++){
            k++;
            if(str[k]!=str[i]) return false;
        }
        if(k==r) return true;
    }
}
int main()
{
    scanf("%s",str+1);
    int n=strlen(str+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=inf;
    for(int i=1;i<=n;i++) dp[i][i]=1;
    for(int p=1;p<=n;p++){
        for(int i=1,j=p+1;j<=n&&i<=n;i++,j=i+p){
            for(int k=i;k<j;k++){
                if(judge(i,j,i,k)) dp[i][j]=min(dp[i][j],dp[i][k]+2+getbit((j-i+1)/(k-i+1)));
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
            }
        }
    }
    printf("%d\n",dp[1][n]);
}

转载于:https://www.cnblogs.com/Chen-Jr/p/11216853.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值