来源:牛客网:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。 压缩后的字符串除了小
写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没
有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程
在这里插入图片描述
另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。

输入描述:

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

输出描述:

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

示例1
输入
复制

bcdcdcdcdxcdcdcdcd

输出
复制

12

题解:

我们先考虑没有M的影响
dp[i][j]表示字符串i到j的最短压缩长度
区间dp,我们可以得到:
第一种:没有相等子串的一般情况时
转移dp [ l ] [ r ] = min ( dp [ l ] [ i ] + dp [ i + 1 ] [ r ] ) (l<=i<=r)
第二种:子串长度是偶数,且可以拆分成两个相等的子串时,此时我们可以进行压缩,
mid=(l+r)>>1,
mid+1到r这一段就可以被一个字符R代替
转移dp[l][r]=min(dp[l][r],f[l][mid]+1)
当考虑有M时,R匹配总是与最近的M匹配
我们可以给状态加一维:
dp[l][r][0/1]表示原串l到r在区间内是否有M的最短长度
如果当前区间左一半和右一半相等且中间没有M,我们可以把后一半换成R(和上面讲的情况一样)
dp[l][r][0]=min(dp[l][r][0],dp[l][i][0]+(r-i))(l<=i<=r)

当区间内有M时,就需要将区间分成M之前和之后两部分压缩,因为R只能匹配最近的M,M之前的就管不了了
然后看拆分后的区间的各自情况(看拆分后的区间是有M好还是没M好),记得要+1,因为多加了一个字符M
dp[l][r][1]=min ( dp [l] [r] [1] , min ( dp[l][i][1] , dp[l][i][0] ) +1)
(l<=i<r)i枚举的是M的位置
以上两步都算是正常情况
当区间[l,r]可以拆分成两个相等的部分[l,mid],[mid+1,r]时,
dp[l][r][0]=min(dp[l][r][0],dp[l][mid][0]+1)
最后答案就是取最小值min(dp[1][n][0],dp[1][n][1])

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=85;
int dp[maxn][maxn][maxn];
int ans[maxn];
inline int read()
{
    int s=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(!isdigit(ch)){s=(s<<3)+(s<<1)+ch-48;ch=getchar();}
    return s*f;
 } 
string s;
bool check(int l,int r)//检验左右区间是否相等 
{
    int mid=(l+r)>>1;
    for(int i=l;i<=mid;i++)
    {
        if(s[i]!=s[mid+i-l+1])return 0;
    }
    return 1;
}
int main()
{

    cin>>s;
    int leng=s.length();
     s=" "+s;
    for(int len=1;len<=leng;++len){//从头开始枚举长度 
        for(int i=1;i+len-1<=leng;++i){
            int j=i+len-1;
            dp[i][j][0]=dp[i][j][1]=len;
            for (int k=i;k<=j;++k){//从k将区间分为两部分 
                dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k);
                dp[i][j][1]=min(dp[i][j][1],min(dp[i][k][1],dp[i][k][0])+min(dp[k+1][j][0],dp[k+1][j][1])+1);
            }
            if(len%2==0&&check(i,j))//如果是偶数,且左右两区间相等
            dp[i][j][0]=min(dp[i][j][0],dp[i][(i+j)>>1][0]+1);
        }
    }
    cout<<min(dp[1][leng][0],dp[1][leng][1]);
    return 0;
}