Processing math: 100%

压缩

压缩( dp)

Descrption

  • 给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不是必需)包含大写字母 RM,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。

  • bcdcdcdcd 可以压缩为 bMcdRR,下面是解压缩的过程:

    已经解压的部分 解压结果 缓冲串
    b ab b
    bM bb
    bMc bc c
    bMcd bcd cd
    bMcdR bcdcd cdcd
    bMcdRR bcdcdcdcd cdcdcdcd
    • 另一个例子是 abcabcdabcabcdxyxyz 可以被压缩为 abcRdRMxyRz

Input

  • 输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n

Output

  • 输出仅一行,即压缩后字符串的最短长度。

Sample Input1

aaaaaaa

Sample Output1

5

Sample Input2

bcdcdcdcdxcdcdcdcd

Sample Output2

12

Hint

  • 样例说明:在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR
  • 数据范围:
  • 50% 的数据满足:1<=n<=20
  • 100% 的数据满足:1<=n<=50

分析

  • dp[i][j][0] 表示表示 ij 的区间内没有 M 的情况:
    • 如果前半段与后半段相等,dp[i][j][0]=min(dp[i][j][0],dp[l][mid][0]+1),(mid=(i+j)/2)
    • 如果[i,j],但有可能 [i,k],(i<k<j) 可以折叠,dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+jk)
  • dp[i][j][1] 表示 ij 的区间内有 M 的情况:
    • k 枚举的是 M 的位置,即在 k 的后面放一个 M:
      • dp[l][r][1]=min(dp[l][r][1],min(dp[l][k][0],dp[l][k][1])+min(dp[k+1][r][0],dp[k+1][r][1])+1)
    • 对区间 [l,k][k+1,r] 均有两种选择,然后加上 M 这个 1
  • 最后输出ans=max(dp[1][n][0],dp[1][n][1])
  • dp[i][j][1] 是两个递归的子问题合并而成,所以套用典型的区间 dp 的模板,注意对 M 的处理。

Code

#include <bits/stdc++.h>
const int maxn=50+5;
char a[maxn];
int dp[maxn][maxn][3];
int n;
int check(int l,int r){//判断前一半是否和后一半相等
    if((r-l+1)&1)return 0;//长度为奇数
    int mid=(l+r)>>1;
    for(int i=l;i<=mid;i++)
        if(a[i]!=a[i+mid-l+1])return 0;
    return 1;
}
void Init(){
    scanf("%s",a+1);
    n=strlen(a+1);
    memset(dp,0x3f,sizeof(dp));
}
void Solve(){
    for(int i=1;i<=n;i++)//初始化为没有折叠
        for(int j=i;j<=n;j++)
            dp[i][j][0]=dp[i][j][1]=(j-i+1);
    for(int d=2;d<=n;d++){//区间dp长度为2开始
        for(int i=1,j;(j=i+d-1)<=n;i++){//i为区间起点,j为区间终点
            if(check(i,j))//区间[i,j]正好能折叠
                dp[i][j][0]=std::min(dp[i][(i+j)/2][0]+1,dp[i][j][0]);
            for(int k=i;k<j;k++)//枚举区间的断点,有可能[i,k]能折叠
                dp[i][j][0]=std::min(dp[i][j][0],dp[i][k][0]+j-k);    
            for(int k=i;k<j;k++)//枚举M的位置,即在k的后面加一个M
                dp[i][j][1]=std::min(dp[i][j][1],std::min(dp[i][k][0],dp[i][k][1])+std::min(dp[k+1][j][0],dp[k+1][j][1])+1);
        }
    }
    printf("%d\n",std::min(dp[1][n][1],dp[1][n][0]));
}
int main(){
    Init();
    Solve();
    return 0;
}
posted @   ♞老姚♘  阅读(202)  评论(0编辑  收藏  举报
编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· 赶AI大潮:在VSCode中使用DeepSeek及近百种模型的极简方法
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
点击右上角即可分享
微信分享提示