Palindrome
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 52154 | Accepted: 17973 |
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.
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
分析:动态规划+滚动空间。
此题很经典,题目花哨,问最少插入多少字符,是该字符串变成回文字符串。
DP。怎么构造? 我的修行不够啊,想不出来啊 F...
有一种是简单DP,dp[i][j]表示字符串从下标i到j 编程回文字符串最少需要插入的字符。状态转移方程如下:
for(int i=n-1;i>=0;i--)
{
for(int j=i+1;j<n;j++)
{
if(str[i] == str[j])
dp[i][j] = dp[i+1][j-1];
else
dp[i][j] = min(dp[i][j-1], dp[i+1][j]) + 1;
}
}
{
for(int j=i+1;j<n;j++)
{
if(str[i] == str[j])
dp[i][j] = dp[i+1][j-1];
else
dp[i][j] = min(dp[i][j-1], dp[i+1][j]) + 1;
}
}
能够得到正解,但是题目的核心不在此,且此解法内存超出 T_T
另辟蹊径>>>
其实和之前的那道DP是类似的,只需将输入的字符串逆序,然后求2个字符串的最长公共子序列的长度y,n-y即为answer。
DP和之前的做法一模一样。但是注意:由于数据范围达到了5000,内存超出。虽然投机取巧的开静态数组用short取代int能够过,但显然考验的不是这个。
隆重推出>>>
滚动数组。注意前提:状态转移方程dp[i][j]只依赖于dp[i-1][j]和dp[i][j-1];运用滚动数组dp[i%2][j] = dp[i%2][j-1] + dp[(i-1)%2][j]);
这样就节省了空间,但是时间上没有节省。
//用dp[5001][5001]内存超出
//必须节省空间,运用滚动数组
#include<iostream>
#include<string>
using namespace std;
/*
int dp[5001][5001] = {0};
int main()
{
int n;
char str[5001];
scanf("%d",&n);
scanf("%s",str);
for(int i=n-1;i>=0;i--)
{
for(int j=i+1;j<n;j++)
{
if(str[i] == str[j])
dp[i][j] = dp[i+1][j-1];
else
dp[i][j] = min(dp[i][j-1], dp[i+1][j]) + 1;
}
}
printf("%d\n",dp[0][n-1]);
return 0;
}
*/
char str1[5001], str2[5001]; // str2为逆序
int dp[2][5001];
int main()
{
int n;
scanf("%d",&n);
scanf("%s",str1);
//逆序保存在str2
for(int i=0;i<n;i++)
str2[i] = str1[n-1-i];
memset(dp, 0, sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
//dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
if(str1[i-1] == str2[j-1])
dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
else
dp[i%2][j] = max(dp[i%2][j-1], dp[(i-1)%2][j]);
}
}
printf("%d\n",n-dp[n%2][n]); // n-dp[n%2][n]
return 0;
}