【POJ】剑指DP:3280.Cheapest Palindrome

0.概述

输入一个只包含英文小写字母的字符串,再输入所有用到的字符的添加代价,删除代价。输出将这个字符串变为回文串的最小代价。

1.分析

好难啊。 不会。
看完discuss更心塞了。在这里插入图片描述
可能这就是智商碾压吧。
这道题我第一反应,和编辑距离那道题差不多。然后我就尝试着按这个套路走。编辑距离有增删改,这题有增有删,没有改,但是改不就是一删一增么?我美滋滋的按这个思路往下走。回想编辑距离那道题,DP表横着是原来的字符串,竖着是目标字符串。这一步我就卡住了——这题的“目标字符串”怎么找?一大堆,无数个,找不出。然后我就束手无策了。
然后去看答案。这道题想做出来,首先要明白一点:
添加和删除是等价的,不需要改。此话怎讲?
比如说我们现在的字符串是ab,想把它变成回文串:
我们可以删除a(b),删除b(a),添加a(aba),添加b(bab)。
既然这样,我们从删除a和添加a中选成本低的,删除b和添加b中也选择成本低的不就得了。
换句话说,所有的“删除a”可以通过“添加a”得到相同的结果,即使得字符串变为回文。毕竟在左侧加一个字符等价于在右边对称的地方删除一个字符。
这个我就没想到。那还做个球。
这个知道了之后那思考维度立刻下降了三倍。接下来考虑递推公式。
DP题啊,核心就是找到两点:DP表的坐标含义,DP元素自身含义。这个找不恰当,那就没得做。
这道题呢?DP表中元素DP[i][j]代表str从i到j,想变成回文串所需要的最小代价。
那么我们怎么求DP[i][j]呢?
我们来举个例子:现在有aba,已经是回文串了,在它后边加个c,abac,这个字符串想变成回文,怎么办?增c删c,压缩以后假设是删c,那么我们想求这个DP,令其为DP[0][3],应该是DP[0][2]+cost[c]。
还没完,我们想得到这个四位的回文串,还有一种方式,先有bac,把它变成回文串,bab,然后前面加个a,abab,这个abab和上面的abac是一个等级的,想把它变成回文,怎么办?增a删a,假设是增a。求DP[0][3],是DP[1][3]+cost[a]。
也就是说。每次在原来的基础上在前面或后面加一个字母,然后新的字符串的代价是原来的代价加上新增字母的代价。
实际做的时候可以采取长度递增的方法,这样做比较直观。
另:用DP,时间复杂度是O(n^2),按理说不会TLE,但是我做的时候TLE了。随后发现是DP表的问题,要用二维数组而不是vector,在这道题中它们的效率差了十倍以上。

三、总结

没啥好总结的,字符串DP做得少了。
PS:代码如下:

#include<iostream>
#include<vector>
#include<string>
#include<cstring>
#include<iterator>
#include<queue>
#include<list>
#include<set>
#include<algorithm>
using namespace std;
//vector<vector<int> > dp(2010, vector<int>(2010, 0));
int dp[2010][2010];
int main()
{
	memset(dp, 0, sizeof(dp));
	int n, m;
	cin >> n >> m;
	string s;
	cin >> s;
	vector<int> um(256,0);
	for (int i = 0; i < n; ++i)
	{
		char a;
		int b, c;
		cin >> a;
		cin >> b >> c;
		um[a] = min(b, c);
	}
	for (int l = 1; l < m; ++l)
	{
		for (int i = 0, j = l; j < m; ++i, ++j)
		{
			dp[i][j] = 0x3f3f3f;
			if (s[i] == s[j])
			{
				dp[i][j] = dp[i + 1][j - 1];
			}
			else
			{
				dp[i][j] = min(dp[i][j - 1] + um[s[j]], dp[i][j]);
				dp[i][j] = min(dp[i+1][j] + um[s[i]], dp[i][j]);
			}
		}
	}
	cout << dp[0][m - 1];
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值