P4302 [SCOI2003]字符串折叠 ---区间dp

这道题目要求求解一个字符串的最短折叠形式。通过定义,字符串折叠是自身连接的串的折叠。题目提供了一个例子说明如何进行折叠,并给出了一种特定情况的示例。解决此问题使用动态规划方法,定义dp[l][r]表示字符串从位置l到r的最短折叠。在转移过程中,检查字符串后半部分是否能被前半部分整除,并使用哈希检查折叠可行性。注意,重复次数的位数可能会改变,需要特别考虑如'AAAAAAAAAA'这样的特殊情况。

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

题目描述

折叠的定义如下:

一个字符串可以看成它自身的折叠。记作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。


挺早以前就准备做的题一直拖到了现在才做。

其实这道题也是中规中矩。我们肯定是找能够折叠的最短,那么dp一定是dp[l][r], 表示l,r区间内的最短折叠,转移就简单了,从l遍历到r看看后半部分能不能整除前半部分,然后用哈希判断一下是不是能叠,能叠的话直接叠起来,要注意的是重复次数的位数是会变的,我原来是直接前半部长度+3,表示加了x,(,),后来看了洛谷评论区发现AAAAAAAAAA (10个)的数据。

#include<bits/stdc++.h>

using namespace std;

#define int long long
int dp[200][200];
long long has[400];
long long pp[500];
const int p = 151;
long long getHas(int l,int r)
{
    return has[r] - has[l-1] * pp[r- l +1];
}

signed main()
{
    char s[200];
    cin >> (s+1);
    pp[0] = 1;
    has[0] = 1;
    for (int i = 1; i <= 200; ++i) {
       pp[i]= pp[i-1] * p;
       has[i] = has[i-1] * p + s[i] - 'A';
    }
    memset(dp,0x3f,sizeof(dp));
    int l = strlen(s+1);
    for (int len = 1; len <= l; ++len) {
        for (int i = 1; i + len - 1<= l; ++i) {
            int end = i+len-1;
            if (len == 1)
            {
                dp[i][end] =1;continue;
            }
            for (int j = i; j < end; ++j) {
                int pre = j - i +1;
                int suf = end - j;
                if (suf%pre == 0)
                {
                    int tmp = getHas(i,j);
                    int nowp = j+1;
                    int nowe = nowp+pre-1;
                    int mul = 1;
                    while (nowe<=end)
                    {
                        if (tmp != getHas(nowp,nowe))
                            break;
                        nowe+=pre;
                        nowp+=pre;
                        mul+=1;
                    }
                    int add = 0;
                    while (mul)
                    {
                        add++;
                        mul/=10;
                    }
                    if (nowe == end + pre) {
                        dp[i][end] = min(dp[i][end],min(dp[i][j]+dp[j+1][end],dp[i][j]+2+add));
                    } else
                        dp[i][end] = min(dp[i][end],dp[i][j]+dp[j+1][end]);

                }else
                    dp[i][end] = min(dp[i][end],dp[i][j]+dp[j+1][end]);
            }

        }
    }
    cout << dp[1][l] << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值