标签:
对应POJ 题目:点击打开链接
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 54734 | Accepted: 18922 |
Description
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; }
标签:
原文地址:http://blog.csdn.net/u013351484/article/details/44726039