首页 > 代码库 > poj 1159 Palindrome

poj 1159 Palindrome

Palindrome
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 53525 Accepted: 18481

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

Sample Output

2

题目意思:给你个字符串,求最少需要添加几个字符使它变成一个回文字符串;

解题思路:求给出的字符串和逆序的字符串的最长公共子序列,用总长度减去这个最长公共子序列的长度即可;

求最长公共子序列(LCS):

字符串:s:  S1S2S3S4.......Si,t:   T1T2T3T4..........Tj;

定义一个数组dp[i][j]表示S1S2S3S4.......Si和T1T2T3T4..........Tj所对应的最长公共子序列;

则S1S2S3S4.......Si+1和T1T2T3T4..........Tj+1所对应的最长公共子序列可能为:

(1)如果Si+1==Tj+1,则S1S2S3S4.......Si和T1T2T3T4..........Tj所对应的最长公共子序列+1;

(2)S1S2S3S4.......Si+1和T1T2T3T4..........Tj所对应的最长公共子序列;

(3)S1S2S3S4.......Si和T1T2T3T4..........Tj+1所对应的最长公共子序列;

所以dp的状态转移方程为dp[i][j]=max( dp[i-1][j-1] + (a[i]==b[j]) , max( dp[i-1][j] , dp[i][j-1] ) )

不过这里依旧存在一个问题那就是dp数组的大小,按照上面的思路我们需要定义dp[5000+1][5000+1],而这远远会超于所给定内存的大小,为此有两种解决办法:

(1)将dp的类型定义为short;

#include <iostream>
#include <string.h>
using namespace std;
#define MAX 5000+1
int n;
char s[MAX];
short dp[MAX+1][MAX+1];

int max(int a,int b){
	return a>b?a:b;
}
void solve(){
	for (int i=0;i<n;i++){
		dp[0][i]=0;
		dp[i][0]=0;
	}
	for (int i=0;i<n;i++) 
		for (int j=0;j<n;j++){
			if (s[i]==s[n-1-j])
				dp[(i+1)][j+1]=dp[i][j]+1;
			else
				dp[(i+1)][j+1]=max(dp[i][j+1],dp[(i+1)][j]);
		}
	cout<<n-dp[n][n]<<endl;
}
int main(){
	
	while (cin>>n){
		for (int i=0;i<n;i++){
			cin>>s[i];
		}
		solve();
	}
	return 0;
}

(2)采用滚动数组;

由状态转移方程dp[i][j]=max( dp[i-1][j-1] + (a[i]==b[j]) , max( dp[i-1][j] , dp[i][j-1] ) )我们可以知道 i 所控制的行的值是由 i-1 和 i 所控制的行的值所确定的,因此我们只需要每回记录前一个的,以便计算当前的,当计算下一个的时候,下一个的存放地址在最前面的地方,(简单说 先记录了a,然后又记录了b,再记录c的时候,把c的存放放到a上,即覆盖a),这样dp的定义就变成了dp[2][5000+1];

#include <iostream>
#include <string.h>
using namespace std;
#define MAX 5000+1
int n;
char s[MAX];
int dp[2][MAX+1];

int max(int a,int b){
	return a>b?a:b;
}
void solve(){
	for (int i=0;i<n;i++){
		dp[0][i]=0;
		dp[1][i]=0;
	}
	for (int i=0;i<n;i++) 
		for (int j=0;j<n;j++){
			if (s[i]==s[n-1-j])
				dp[(i+1)%2][j+1]=dp[i%2][j]+1;
			else
				dp[(i+1)%2][j+1]=max(dp[i%2][j+1],dp[(i+1)%2][j]);
		}
	cout<<n-dp[n%2][n]<<endl;
}
int main(){
	
	while (cin>>n){
		for (int i=0;i<n;i++){
			cin>>s[i];
		}
		solve();
	}
	return 0;
}




poj 1159 Palindrome