POJ 1159--Palindrome(回文序列)

题目要求对于一序列至少加上多上字符能组成一个回文序列,可参考算法导论中LCS的DP证明方法。

设序列为a[i],b[i][j]为a[i]到a[j]组成的子串插入最少的字符构成的回文序列,设dp[i][j]为最少需要插入字符的数目。

首先证明b[i][j]的始终字符(肯定相同)必定是a[i],a[j]当中其中一个。

反证:若始终字符为x0,b[i][j] = x0 x1 ...xk a[i] ...a[i] xk ... x1 x0或者x0 x1 .. xk a[j] a[i] .. a[i] a[j] xk .. x1 x0,则我们剥离所有x字符,组成的序列是一个需要插入字符更少的b[i][j],矛盾。

  1. i =j,dp[i][j] = 0
  2. a[i] = a[j],则dp[i][j] = dp[i+1][j-1]。同样用反证法,若dp[i][j] < dp[i+1][j-1],则我们可以去除b[i][j]两头的a[i](前面已证),得到一个由a[i+1]到a[j-1]新构成的回文序列,此新序列需要插入的字符数等于dp[i][j],且小于dp[i+1][j-1],矛盾。
  3. 若a[i] != a[j],假设b[i][j]的头尾字符为a[i],则我们去除头尾的a[i],得到一个由a[i+1]到a[j]新构成的回文序列,易知此序列为b[i+1][j],所以dp[i][j] = dp[i+1][j]+1;同理可证当b[i][j]的头尾为a[j]时,dp[i][j] = dp[i][j-1]+1。
  4. 由3可知,当a[i] != a[j]时,dp[i][j] = min{dp[i+1][j],dp[i][j-1]}+1
根据转移方程,易知可以使用滚动数组优化内存。
注意:当j = i+1时,dp[i][j] = dp[i+1][i],若不使用滚动数组,等式右边的值会一直为0,正确。若使用滚动数组,则需要在每次输入更新时初始化滚动数组为0,因为不初始化会使用上次输入的数组值。


#include<cstdio>
#include<cstring>
#define maxN 6001
#define _min(x,y) ((x)<(y)?(x):(y))

int main()
{
    int i,j,N;
    char flag;
    char str[maxN];
    short minNum[2][maxN];
    while(~scanf("%d",&N))
    {
        getchar();
        gets(str);
        memset(minNum,0,sizeof(minNum));
        flag = 1;
        for(i = N-1;i >= 0;i--)
        {
            for(j = i+1;j < N;j++)
            {
                if(str[i] == str[j])
                    minNum[flag][j] = minNum[!flag][j-1];
                else
                    minNum[flag][j] = _min(minNum[!flag][j],minNum[flag][j-1])+1;
            }
            flag = !flag;
        }
        printf("%d\n",minNum[!flag][N-1]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值