题目描述:
给定一个字符串S,可以通过在字符串的任意位置插入字符,使其变为回文串。求最少插入字符的数量。
题目来源:https://vjudge.net/problem/POJ-1159
解法1:
求出S中,是回文的最长子序列L,那么结果ans = length(S) – length(L),解法如下:
1. 求出S的逆序串S‘
2. 求出S和S’的最长公共子序列L
3. ans = length(S) – length(L)
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5001;
int dp[2][N];
int getLSCLength(int len, string s1, string s2) {
memset(dp, 0, sizeof(dp));
int e = 0;
for (int i = 1; i <= len; i++) {
e = 1 - e; // 这里用了滚动数组,滚动数组是为了节省空间,因为每次要用到的只是上一轮大循环的数据,而无需保存再之前的数据
for (int j = 1; j <= len; j++) {
if (s1[i-1] == s2[j-1]) {
dp[e][j] = dp[1-e][j-1] + 1;
} else {
dp[e][j] = max(dp[1-e][j], dp[e][j-1]);
}
}
}
return dp[e][len];
}
int main() {
int n;
string s;
cin >> n >> s;
string t(s.rbegin(), s.rend());
int lsc = getLSCLength(n, s, t);
cout << s.length() - lsc << endl;
}
解法2:
采用动态规划,主要从两端字符进行比较,dp[i][j]表示下标i到j的子串要成为回文串所需插入的字符串的长度
如果 s[i] = s[j],那么 dp[i][j] = dp[i+1][j-1]
否则,dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1,为什么是dp[i+1][j]和dp[i][j-1]呢,因为每次发现头尾不匹配时,这时在头部或者尾部添加对应的却掉的字符就可达到构造回文串的目的,对于这其中的每个子问题都是这样的
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_SIZE = 5005; //最大字符串长度
short dp[MAX_SIZE][MAX_SIZE]; //dp[i][j]存储字符串[i, j]要插入的字符数,使用得其成为“回文”
int main()
{
int n;
cin >> n;
string str;
cin >> str;
/*循环求得的是一个右上角矩阵,其余元素为0,这样做的原因是在解决子问题重复的问题*/
for(int i = n - 1; i >= 0; i--) {
for(int j = i; j <= n - 1; j++) {
if(str[i] == str[j])
dp[i][j] = dp[i + 1][j - 1];
else
dp[i][j] = getMin(1 + dp[i][j-1], 1 + dp[i+1][j]);
}
}
cout << dp[0][n-1] << endl;
return 0;
}