回文 Palindrome 最长公共子序列 滚动数组

Palindrome
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 49842 Accepted: 17150

Description

A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a program which, given a string, determines the minimal number of characters to be inserted into the string in order to obtain a palindrome. 

As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome. 

Input

Your program is to read from standard input. The first line contains one integer: the length of the input string N, 3 <= N <= 5000. The second line contains one string with length N. The string is formed from uppercase letters from 'A' to 'Z', lowercase letters from 'a' to 'z' and digits from '0' to '9'. Uppercase and lowercase letters are to be considered distinct.

Output

Your program is to write to standard output. The first line contains one integer, which is the desired minimal number.

Sample Input

5
Ab3bd
 
 
 
题意:给定一个字符串,求对它最少进行多少次操作,可以变成一个回文串
有这么个结论,需要加的字符的个数=原来字符串的长度-原来字符串和逆字符串的最长公共子序列的长度。
然后用滚动数组即可。
#include<iostream>  
#include<cstring>  
#define N 5010  
using namespace std;
int dp[2][N];  
char s1[N],s2[N];   
int max(int a,int b)  
{  
    return a>=b?a:b;  
}  
  
int main()  
{  
    int n,i,j;    
    while(cin>>n)  
    {  
        cin>>s1+1;
		for(i=1;i<=n;i++)
			s2[n-i+1]=s1[i];
		memset(dp,0,sizeof(dp)); 
        for(i=1;i<=n;i++)  
        {  
            for(j=1;j<=n;j++)  
            {  
                if(s1[i]==s2[j])   
                    dp[i%2][j]=dp[(i-1)%2][j-1]+1;  
                else
					dp[i%2][j]=max(dp[(i-1)%2][j],dp[i%2][j-1]);   
            }  
        }  
        cout<<n-dp[n%2][n]<<endl;  
    }  
    return 0;  
}  

另一种解法:

动态规划法。设字符串为S,长度为L,d[i][j]表示以第i个字符为首,第j个字符为尾的字符串构成回文最少需要添加的字符个数,i和j的初值分别为1、L。

如果S[i] == S[j],即字符串两端的字符相等,d[i][j] = d[i+1][j-1],

即d[i][j]等于去掉头尾后的字符串的d值。

如果S[i] != S[j],此时划分出两个子问题,求d[i][j-1]和d[i+1][j],它两中较小的值再加1即为d[i][j](加上的1个字符是用于和S[i]或者S[j]构成对称的)。

状态转移方程:

从上面的分析可以看出,这个问题的实质是求最长公共子序列,只是这两个序列分别是串S的前一部分和串S后一部分的逆序列。
#include <iostream>  
#include <cstdio>  
#include <cstring>  
  
using namespace std;  
  
#define MIN(a, b) ((a) < (b) ? (a) : (b))  
  
#define MAXSIZE 5005  
  
//开始没有考虑内存问题,使用了int型,超内存限制,也可使用滚动数组解决  
unsigned short d[MAXSIZE][MAXSIZE];  
  
int ToPalindrome(char *s, int n)  
{  
    int i, j, k;  
    //只有一个字符时,不需要添加字符  
    for (i = 0; i < n; i++)  
    {  
        d[i][i] = 0;  
    }  
    //串长度为2时  
    for (i = 1; i < n; i++)  
    {  
        if (s[i-1] == s[i])  
        {  
            d[i-1][i] = 0;  
        }  
        else  
        {  
            d[i-1][i] = 1;  
        }  
    }  
  
    //串长度递增  
    for (k = 2; k < n; k++)  
    {  
        for (i = 0, j = k; j < n; i++, j++)  
        {  
            if (s[i] == s[j])  
            {  
                d[i][j] = d[i+1][j-1];  
            }  
            else  
            {  
                d[i][j] = MIN(d[i][j-1], d[i+1][j]) + 1;  
            }  
        }  
    }  
    return d[0][n-1];  
}  
  
int main(void)  
{  
    char str[MAXSIZE];  
  
    int n;  
    while (scanf("%d", &n) != EOF)  
    {  
        getchar();  
        gets(str);  
        printf("%d\n", ToPalindrome(str, n));  
    }  
    return 0;  
}  


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值