Palindrome
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 Source |
大致题意:
给出字符串和字符串的长度,问该字符串变为回文字符串最少需要添加的字符数。
分析:
做这题有两种动态规划的方法,一是直接对它和它的逆序串求最长公共子序列长度len。n-len即为所求。(n为原串长度)。
另外一种动态规划的做法是
dp[i][j]表示从 i 到 j 这段子串若变为回文串最少添加的字符数。
if (st[i] == st[j])
dp[i][j] = dp[i + 1][j - 1];
else
dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;
但是两种方法都需要用到滚动数组节约内存!!
因为题目给的是65536KB的内存,只能开1677W的int数组,而5000*5000的dp数组有2500W显然超内存了。会TLE的。但如果用short int就可以过。。(因为目前流行的32位C++编译器中,通常int占4字节,short int占2字节。其中short int可以简写为short。)
但还是要学一下滚动数组。。。
根据dp滚动的过程我们可以知道,dp【i】【j】的值不会与dp[i-2][0.....n]的值有关系。
那么可以把dp[i][j]的值覆盖到dp[i-2][j]上。即dp[i][j]为dp[i%2][j];
下面的法一是用动态数组和LCS的。法二是用short int和直接dp求子串变为回文序列最少需要添加的字符数的。
法一:
#include
#include
#define max 5010
int dp[2][max];//dp[i][j]表示正序的前i个(0~i-1)(这里还要取余2)和逆序的前j个(0~j-1)的最长公共子串长度。
char s1[max],s2[max];
int main()
{
int n;
while( scanf("%d",&n) != EOF)
{
scanf("%s",s1);
int len = strlen(s1);
for(int i = len - 1; i >= 0; i--)
{
s2[len - i - 1] = s1[i];
}
s2[len] = '\0';
memset(dp,0,sizeof(dp));//全部初始化为0
for( int i = 1; i <= len; i++ )
{
for( int j = 1; j <= len; j++ )
{
if(s1[i-1] == s2[j-1])
dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
else
dp[i%2][j] = dp[(i-1)%2][j] > dp[i%2][j-1] ? dp[(i-1)%2][j] : dp[i%2][j-1];
}
}
int ans = n - dp[n%2][n];
printf("%d\n",ans);
}
}
法二:
#include
#include
#include
#include
using namespace std;
#define maxn 5010
char s[maxn];
int n;
short dp[maxn][maxn];//dp[i][j]表示从i到j这段子串若变为回文串最少需要添加的字符数
int main()
{
scanf("%d", &n);
scanf("%s", s);
for (int i = n -1; i >=0; i--)//从最后往前推
{
dp[i][i] =0;
for (int j = i +1; j < n; j++)
{
if (s[i] == s[j])
{
dp[i][j] = dp[i +1][j -1];//往内压缩,i+1 ~ n-1的肯定已经计算完了
}
else
{
dp[i][j] = min(dp[i +1][j], dp[i][j -1]) +1;//i是逆推的,所以i+1已经计算好了,
//j是顺推的,所以j-1已经计算好了。
}
}
}
printf("%d\n", dp[0][n -1]);
return 0;
}