对应POJ 题目:点击打开链接
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 54734 | Accepted: 18922 |
Description
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
Output
Sample Input
5 Ab3bd
Sample Output
2
题意:给出长度为n的字符串,求最少添加多少个字符就能把原字符串变成回文串(添加的位置是任意的)
思路:最少添加数量 = 原串长度 - 原串跟其逆序串的最长公共子序列长度。
证明:原串跟其逆序串的最长公共子序列 = 原串的最长不连续回文串(比如 aABdCBcAm 的最长不连续回文串是ABCBA),既然最长不连续回文串知道了,那在剩下的每个字符的对称位置添加该字符,就能把原串变成回文串。而剩下的字符个数也就是题目的答案,接下来不言而喻。。。
要注意的是,求最长公共子序列时需要把空间压缩成一维。使用到一些技巧:
0 1 2 3 4 5 j ->
0 0 0 0 0 ...
1 0 a b ...
2 0 c d ...
i 0 ...
dp[i][j] 表示A串前i个字符跟B串前j个字符的最长公共子序列。转移方程为
if(A[i-1] == B[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
通俗地讲就是如果A[i] == B[j],那dp[i][j] 就等于它左上角的值加1,否则dp[i][j] 就等于它左边跟它上面的值中较大的那个。
仔细想想我们就能把不必要的空间消耗省掉,具体看代码。。。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define M 5010
using namespace std;
char s1[M];
char s2[M];
int dp[M];
int main()
{
//freopen("in.txt", "r", stdin);
int n;
int i, j, t, tmp;
while(~scanf("%d", &n))
{
memset(dp, 0, sizeof(dp));
scanf("%s", s1);
for(i=0; i<n; i++)
s2[n-i-1] = s1[i];
for(i=0; i<n; i++){
tmp=0;
for(j=0; j<n; j++){
t=dp[j+1];
if(s1[i] == s2[j]) dp[j+1] = tmp+1;
else if(dp[j] > t) dp[j+1] = dp[j];
tmp=t;
}
}
printf("%d\n", n - dp[n]);
}
return 0;
}