题目的大概意思就是给你一个字符串,然后可以往字符串里面插入字符,要计算出至少插入多少个字符,可以让它形成一个回文串。
做法就是生成一个新串,该新串是原串的逆序串,然后求两个串的最长公共子序列就好。
动归方程:
dp[i][j] = max( dp[i-1][j], dp[i][j-1], dp[i-1][j-1] + ( s1[i] == s2[j] ? 1 : 0 ) );
动归求最长公共子序列还可以转化为求最长上升子序列,就是找到第一个串中的每个字符在第二串中出现的位置,然后逆序,求出最长上升子序列(绝对上升)。学习这种方法的链接
另外题目直接开dp[5001][5001]会MLE,所以要使用滚动数组。
#include "stdio.h"
#include "string.h"
#include "math.h"
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAXM 1
#define MAXN 1
#define max(a,b) a > b ? a : b
#define min(a,b) a < b ? a : b
#define Mem(a,b) memset(a,b,sizeof(a))
int Mod = 1000000007;
double pi = acos(-1.0);
double eps = 1e-6;
typedef struct{
int f,t,w,next;
}Edge;
Edge edge[MAXM];
int head[MAXN];
int kNum;
void addEdge(int f, int t, int w)
{
edge[kNum].f = f;
edge[kNum].t = t;
edge[kNum].w = w;
edge[kNum].next = head[f];
head[f] = kNum ++;
}
int dp[2][5005];
char s1[5005], s2[5005];
int N;
void solve(){
getchar();
scanf("%s", s1);
for(int i = 0; i < N; i ++){
s2[i] = s1[N-1-i];
}
s2[N] = 0;
for(int i = 0; i <= N; i ++){
dp[0][i] = 0;
}
for(int i = 1; i <= N; i ++){
dp[i&1][0] = 0;
for(int j = 1; j <= N; j ++){
dp[i&1][j] = 0;
if( s1[i-1] == s2[j-1] ){
dp[i&1][j] = dp[(i-1)&1][j-1] + 1;
}
else{
dp[i&1][j] = dp[(i-1)&1][j-1];
}
dp[i&1][j] = max(dp[i&1][j], dp[i&1][j-1]);
dp[i&1][j] = max(dp[i&1][j], dp[(i-1)&1][j]);
}
}
printf("%d\n",N - dp[N&1][N]);
}
int main()
{
// freopen("d:\\test.txt", "r", stdin);
while(cin>>N){
solve();
}
return 0;
}