字符串折叠(区间dp)

题目链接:https://ac.nowcoder.com/acm/problem/20238

题目描述

折叠的定义如下:

一个字符串可以看成它自身的折叠。记作S = S X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
如果A = A’, B = B’,则AB = A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) =
AAACBB,而2(3(A)C)2(B) = AAACAAACBB

给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。

输入描述:

仅一行,即字符串S,长度保证不超过100。

输出描述:

仅一行,即最短的折叠长度。

示例1
输入
复制

NEERCYESYESYESNEERCYESYESYES

输出
复制

14

思路:
#include<bits/stdc++.h>
#include<cstdio>
#define ll long long
using namespace std;
const int MMAX=2e2+5;
char s[MMAX];
int dp[MMAX][MMAX];
bool check(int l1,int r1,int l2,int r2) ///[l1,r1]是主串
{
    ///遍历判断是否是主串的子串
    int rri=r2-l2+1;
    for(int i=l1;i<=r1;i++)
    {
        int lle=(i-l1)%rri;
        if(s[i]!=s[l2+lle])
            return false;
    }
    return true;
}
int dfs(int l,int r)
{
    int len=r-l+1;
    for(int le=1;le<=len;le++)
    {
        if(len%le==0)
        {
            if(check(l,r,l,l+le-1))
                return le; ///寻找最小子串
        }
    }
}
int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=len;i++) dp[i][i]=1;
    for(int le=2;le<=len;le++) ///枚举长度
    {
        for(int l=1;l<=len-le+1;l++) ///枚举起点
        {
            int r=l+le-1; ///确定终点
            dp[l][r]=r-l+1; ///确定初始值
            for(int k=l;k<r;k++) ///枚举断点
            {
                int len1=k-l+1,len2=r-k;
                if(len1%len2==0)  ///len1>len2
                {
                    if(check(l,k,k+1,r)) ///判断len2是否是len1的子串
                    {
                        int su1=dfs(k+1,r);
                        int su2=(len1+len2)/su1;
                        int su3=(len1+len2)/min(len1,len2);
                        
                        ///最小子串合并
                        if(su2>=100) dp[l][r]=min(dp[l][r],su1+5);
                        else if(su2>=10) dp[l][r]=min(dp[l][r],su1+4);
                        else dp[l][r]=min(dp[l][r],su1+3);
                        
                        ///整个字符串整体合并
                        if(su3>=100) dp[l][r]=min(dp[l][r],dp[k+1][r]+5);
                        else if(su3>=10) dp[l][r]=min(dp[l][r],dp[k+1][r]+4);
                        else dp[l][r]=min(dp[l][r],dp[k+1][r]+3);
                    }
                    else dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
                }
                else if(len2%len1==0) ///len2>len1
                {
                    if(check(k+1,r,l,k)) ///判断len1是否是len2的子串
                    {
                        int su1=dfs(l,k);
                        int su2=(len1+len2)/su1;
                        int su3=(len1+len2)/len1;
                        
                        ///最小子串合并
                        if(su2>=100) dp[l][r]=min(dp[l][r],su1+5);
                        else if(su2>=10) dp[l][r]=min(dp[l][r],su1+4);
                        else dp[l][r]=min(dp[l][r],su1+3);
                        
                        ///整个字符串整体合并
                        if(su3>=100) dp[l][r]=min(dp[l][r],dp[l][k]+5);
                        else if(su3>=10) dp[l][r]=min(dp[l][r],dp[l][k]+4);
                        else dp[l][r]=min(dp[l][r],dp[l][k]+3);
                    }
                    else dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
                }
                else dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
            }
        }
    }
    printf("%d\n",dp[1][len]);
    return 0;
}

 

posted @ 2020-10-21 19:48  小垃圾的日常  阅读(226)  评论(0)    收藏  举报