问题:回文子序列个数
题目描述
求字符串中回文子序列的个数。(如CDC的回文子序列包括C,D,C,CC,CDC,共5个,位置不同的相同序列需要重复计数)
分析
定义状态
思考的方式几乎与求解最长回文子序列相同,由于最后需要统计个数,所以我们需要将状态定义为:表示位置ii,j之间回文子序列的个数
状态转移
我们同样需要考虑在位置i,j之间的回文子序列的个数是由哪几种情况转化过来的
(1)在位置i+1,j之间的回文子序列个数,添加了第i位
(2)在位置i,j-1之间的回文子序列个数,添加了第j位
但这两种存在着叠加了两次的公共部分,即为位置i+1,j-1之间的回文子序列个数;不过此部分在第i位与第j位字符相同的情况下时,还是需要再添加上1,因为原有的回文子序列左右两端同时加上两个一样的字符还会构成回文子序列,并且其本身也会构成一个回文子序列。所以总结起来,其状态转移方程如下:
边界条件
同最长回文子序列,起始状态依然是任意位置都为一个回文子序列,即,所求值为。
求解方法
递归
int CPC(vector<char>& s, int i, int j) {
if (i == j) {
return 1;
}
if (i > j) {
return 0;
}
if (s[i] == s[j]) {
return CPC(s, i + 1, j) + CPC(s, i, j - 1) + 1;
}
return CPC(s, i + 1, j) + CPC(s, i, j - 1) - CPC(s, i + 1, j - 1);
}
普通的递归求解办法,但是由于递归深度的限制此思路仅仅要求了解即可。
动态规划(打表法)
int CPC(vector<char>& s, int n) {
vector<vector<long>> res(n, vector<long>(n, 0));
for (int i = 0; i < n; i++) {
res[i][i] = 1;
}
for (int i = 1; i < n; i++) {
long count = 0;
for (int j = i - 1; j >= 0; j--) {
if (s[j] == s[i]) {
count = (res[j + 1][i] + res[j][i - 1] + 1);
}
else {
count = (res[j + 1][i] + res[j][i - 1] - res[j + 1][i - 1]);
}
res[j][i] = count;
}
}
return res[0][n - 1];
}
使用记忆化搜索的动态规划方法可以减少递归查找的迭代深度,避免了超时错误的出现。
时间复杂度
根据搜索过程可以判断出时间复杂度为。