问要将给定字符串变为回文字符串,最少插入多少个字符。
难点在于想清楚“如何转变为回文字符串”,若为回文字符串,则正序逆序完全相同,也就是lcs长度等于字符串长度。
所以如果要求插入字符的数量,就是正序逆序串lcs的长度减去字符串的长度。
还有一个问题是,如果直接这么写空间是不够的。要用到滚动数组。
因为dp是上下状态之间的转移,所以只需要有两个位置,一个保存上一状态,一个用于通过上一状态计算出当前的状态(也就是只需要用2个位置即可,开一个二维数组,用e来标志当前与上一状态,e=1-e实现状态之间的转换即可)。这个优化方法很好,学到惹。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
/*3 <= N <= 5000*/
#define MAX_N 5000
string origin, revo;
int dp[2][MAX_N+1];
int main()
{
int length = 0, lcs = 0, ans = 0;
cin >> length >> origin;
/*
//特判所有字符均相同
char contain = origin[0];
for(int i = 1;i < length;i++)
if(origin[i]!=contain)
{
flag = 1;
break;
}
if(!flag)
ans = 0;
//dp[i] 表示第i长度min insert
else{
dp[1] = dp[0] = 0;
for(int i = 1;i < length;i++)
{
//找全部字符的奇偶
int cnt = 0;
for(int k = 0;k < length;k++)
if(origin[i] == origin[k])
cnt++;
if((cnt-1) != 0 && (cnt-1)%2 == 0)
dp[i] = dp[i-1];
else
dp[i] = dp[i-1]+1;
}
//all in-odd / only 1 odd
ans = dp[length-1];
}
printf("%d", ans);*/
//correct ans
revo = origin;
reverse(revo.begin(), revo.end());
int e = 0;
//get revo origin lcs
for(int i = 1;i <= length;i++)
{
//cout << "loop" << endl;
e = 1-e;//1
for(int j = 1;j <= length;j++)
{
//cout << "inner" << endl;
if(revo[i-1] == origin[j-1])//position difference
dp[e][j] = dp[1-e][j-1] + 1;
else
dp[e][j] = max(dp[1-e][j], dp[e][j-1]);
}
}
lcs = dp[e][length];
ans = length - lcs;
printf("%d\n", ans);
return 0;
}