POJ1159 Palindrome

转载自     金海峰的博客



题意:求对字符串最少添加几个字符可变为回文串。

分析:简单做法是直接对它和它的逆序串求最长公共子序列长度len。n-len即为所求。(n为原串长度)

这样做的原因如下:

要求最少添加几个字符,我们可以先从原串中找到一个最长回文串,然后对于原串中不属于这个回文串的字符,在它关于回文串中心的对称位置添加一个相同字符即可。那么需要添加的字符数量即为n-最长回文串长度。

最长回文串可以看作是原串中前面和后面字符的一种匹配(每个后面的字符在前面找到一个符合位置要求的与它相同的字符)。这种的回文匹配和原串与逆序串的公共子序列是一一对应的(一个回文匹配对应一个公共子序列,反之亦然),而且两者所涉及到的原串中的字符数量是相等的,也就是最长公共子序列对应最长回文串。原因陈述完毕。



还有另一个动态规划的方法。

f[i][j]表示从i到j这段子串若变为回文串最少添加的字符数。

if (st[i] == st[j])
f[i][j] = f[i + 1][j - 1];
else
f[i][j] = min(f[i + 1][j], f[i][j - 1]) + 1;


但是我在别人博客上面看到一段话 对上面的有了新的疑问:

引用自    鼻子很帅的猪   的博客:

  最长回文子串是最初我在网易笔试的时候遇见的,当时天真的把原字符串S倒转过来成为S‘,以为这样就将问题转化成为了求S和S’的最长公共子串的问题,而这个问题是典型的DP问题,我也在前面的文章中介绍了3中解决这个问题的方法。但是非常可惜,后来才知道这个算法是不完善的。那么到底为什么呢?听我慢慢道来。

S=“c a b a”  那么  S' = “a b a c”, 这样的情况下 S和 S‘的最长公共子串是aba。没有错误。

    但是当 S=“abacdfgdcaba”, 那么S’ = “abacdgfdcaba”。 这样S和S‘的最长公共子串是abacd。很明显abacd并不是S的最长回文子串,它甚至连回文都不是。

    现在是不是都明白为什么最长回文子串不能转化成为最长公共子串问题了。当原串S中含有一个非回文的串的反序串的时候,最长公共子串的解法就是不正确的。正如上一个例子中S既含有abacd,又含有abacd的反串cdaba,并且abacd又不是回文,所以转化成为最长公共子串的方法不能成功。除非每次我们求出一个最长公共子串的时候,我们检查一下这个子串是不是一个回文,如果是,那这个子串就是原串S的最长回文子串;如果不是,那么就去求下一个次长公共子串,以此类推。


后来我仔细一想,这个问题跟求最长回文字串有关系但并不一样,上面的公式确实是正确的。



最后,我还找到了这样一个代码:

来自   博客


下面这个是用short代替int过的方法,我还没搞清楚为什么short能过


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
usingnamespace std;

#define maxn 5005

char st[maxn];
int n;
short f[maxn][maxn];

int main()
{
    //freopen("t.txt", "r", stdin);
    scanf("%d", &n);
    scanf("%s", st);
    for (int i = n -1; i >=0; i--)
    {
        f[i][i] =0;
        for (int j = i +1; j < n; j++)
            if (st[i] == st[j])
                f[i][j] = f[i +1][j -1];
            else
                f[i][j] = min(f[i +1][j], f[i][j -1]) +1;
    }
    printf("%d\n", f[0][n -1]);
    return0;
}
下面的是滚动数组DP
</pre><pre name="code" class="cpp"><pre name="code" class="cpp">#include<stdio.h>
#include<string.h>
#define size 5010
int n,dp[2][size];
char a[size];
int min(int a,int b)
{
	return a>b?b:a;
}
int main()
{
	int i,j,k,l,m;
	while(scanf("%d",&n)!=EOF)
	{
		memset(a,0,sizeof(a));
		memset(dp,0,sizeof(dp));
		getchar();
		scanf("%s",&a);
		for(i=n-2;i>=0;i--)
		{
			for(j=i+1;j<n;j++)
			{
				if(a[i]==a[j])
					dp[i%2][j]=dp[(i+1)%2][j-1];
				else
					dp[i%2][j]=min(dp[(i+1)%2][j],dp[i%2][j-1])+1;
			}
		}
		printf("%d\n",dp[0][n-1]);
	}
	return 0;
}


 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值