poj 1159 Palindrome

本文介绍了一种通过计算字符串及其逆序字符串的最长公共子序列来确定将字符串转换为回文所需的最少字符插入数量的方法。该方法利用动态规划算法,并提供两种优化方案:使用short类型减少内存消耗和应用滚动数组技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值