题意:
给与一个长度不超过50的字符串,它可以按下列方式压缩:
图片说明
M是开始,R是重复从前一个M开始的字符串。
求字符串压缩后的最短长度。

思路:
区间dp:
dp[i][j][0/1]:dp[i][j][0]表示从i到j这段字符串中没有有M、dp[i][j][1]表示从i到j这段字符串中可以出现M的最少字符数。
当一段字符串可以被压缩时:
dp[i][j][0]=min(dp[i][j][0],dp[i][i+k/2-1][0]+1);(无M才可以被压缩)
字符串更新时:
dp[i][j][0]=min(dp[i][j][0],dp[i][x][0]+(j-x)(后一段的字符数))(x>=i&&x<=j)(合并成无M的字符串,前一段可以被压缩,而后一段一定没被压缩)
dp[i][j][1]=min(dp[i][j][1],dp[i][x][1]+dp[x+1][j][1]+1)(二段可以出现M的字符串合并时,中间需要加一个M间隔)
dp[i][j][1]=min(dp[i][j][1],dp[i][j][0]);(可以出现的情况分为出现和不出现)

代码:

#include <bits/stdc++.h>
#define inf 1000000007
#define eps 0.000001
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int dp[105][105][2];

int main()
{
    string s;
    cin >> s;
    int n=s.length();
    for(int k=1; k<=n; k++)
    {
        for(int l=0; l+k-1<n; l++)
        {
            int r=l+k-1;
            dp[l][r][0]=k;
            dp[l][r][1]=k;
            if(k%2==0)
            {
                int p=0;
                for(int i=l,j=l+k/2; i<l+k/2; i++,j++)
                {
                    if(s[i]==s[j])
                    {
                        ;
                    }
                    else
                    {
                        p=1;
                        break;
                    }
                }
                if(!p)
                {
                    dp[l][r][0]=min(dp[l][r][0],dp[l][l+k/2-1][0]+1);
                }
            }
            for(int x=l; x<=r; x++)
                dp[l][r][0]=min(dp[l][r][0],dp[l][x][0]+r-x);
            for(int x=l; x<=r; x++)
                dp[l][r][1]=min(dp[l][r][1],dp[l][x][1]+dp[x+1][r][1]+1);
            dp[l][r][1]=min(dp[l][r][1],dp[l][r][0]);
        }
    }
    printf("%d\n",dp[0][n-1][1]);
    return 0;
}