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

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

传送门
题目描述
折叠的定义如下:

一个字符串可以看成它自身的折叠。记作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
输出 #1 复制
14
说明/提示
一个最短的折叠为:2(NEERC3(YES))

题解
1.算法思路
(1)dp核心:
看到区间求解会顺理成章的想到区间dp,将问题转换成多个子问题求解
根据区间dp通过子问题求解的特性
我们可以写出换与不换两种dp方程,如下

//k是区间i到j中间枚举出的一个点
dp[i][j]=min(dp[i][j],dp[i][k]+2+solve(l/(k-i+1)));
//2是括号的长度,solve是用来求系数的长度,比如系数为39,solve就会返回2
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);//直接通过子问题转移求解

这里我们需要注意的是不一定折叠了就会更优,如 aaa折叠为3(a)就会更长
(2)check:
我们的第一个dp的使用是有条件的---------能够折叠
所以在dp过程中我们需要先check一下能否折叠

int check(int x1,int y1,int x2,int y2)
{
	if((y2-x1+1)%(y1-x1+1)!=0) return 0;//判断能否被折叠成这个长度为y1-x1+1的片段
	int h=y1-x1+1;//将要折叠成为的长度
	for(int i=x2;i<=y2;i++)
	{
		if(v[i]!=v[i-h]) return 0;
		//这里其实还是有一些投机的,不需要改变数组初始位置以及取模来与第一个模板比较,而是可以层层check
	}
	return 1;//如果可以就return true
}

(3)dp初始化:这是一个很重要的细节点,单列一波。
因为求小,先直接memset为较大数。
然后因为每个区间长度唯一的dp数组都在开始一定为1(因为只一个字符嘛)
然后dp[i][j]因为表示的是从i到j的区间内的字符串最短长度,所以应该初始化为初始的字符串长度,即j-i+1

2.附上AC代码+注释

#include<cstdio>
#include<iostream>
#include<stack>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 105
#define ll long long int 
using namespace std;
char v[maxn];
int dp[maxn][maxn];
int check(int x1,int y1,int x2,int y2)
{
	if((y2-x1+1)%(y1-x1+1)!=0) return 0;
	int h=y1-x1+1;
	for(int i=x2;i<=y2;i++)
	{
		if(v[i]!=v[i-h]) return 0;
	}
	return 1;
}
int solve(int k)
{
	int su=0;
    while(k)
    {
        su++;
        k/=10;
    }
    return su;
}
int get[105];
int len;
int main()
{	
	memset(dp,0x3f,sizeof(dp));
	cin>>v;
	len	=strlen(v);
	for(int i=0;i<len;i++)
		dp[i][i]=1;
	
	for(int l=2;l<=len;l++)
	{
		for(int i=0,j;(	j=l+i-1)<len;i++)
		{
			dp[i][j]=j-i+1;
			for(int k=i;k<j;k++)
			{
				k-i+1;
				if(check(i,k,k+1,j))
				{
					dp[i][j]=min(dp[i][j],dp[i][k]+2+solve(l/(k-i+1)));
				}
				else
				{
					dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);//这里也可以拿出去,不过更加麻烦
				}
			}
		}
	}
	cout<<dp[0][len-1];
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值