leetcode刷题笔记——01
730. 统计不同回文子序列
给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模。
通过从 S 中删除 0 个或多个字符来获得子序列。
如果一个字符序列与它反转后的字符序列一致,那么它是回文字符序列。
如果对于某个 i,A_i != B_i,那么 A_1, A_2, … 和 B_1, B_2, … 这两个字符序列是不同的。
字符串 S 的长度将在[1, 1000]范围内。
每个字符 S[i] 将会是集合 {‘a’, ‘b’, ‘c’, ‘d’} 中的某一个。
思路分析
记dp[x][i][j]表示子字符串S[i~j]中不同回文子序列的数目,其中(0<x<4),s[i]=s[j]=a+‘x’,
1、当s[i] !=a+'x’时,dp[x][i][j]=dp[x][i+1][j]
2、当s[j] !=a+'x’时,dp[x][i][j]=dp[x][i][j-1]
3、当s[i]==s[j]==a+'x’时,dp[x][i][j]=2+dp[0][i+1][j-1]+ dp[1][i+1][j-1] + dp[2][i+1][j-1] + dp[3][i+1][j-1],
当第一个和最后一个字符相同时,我们需要计算子串 S[i+1][j-1] 中所有不同的回文(a、b、c、d 中的每一个),因为dp[x]表示的是以a+'x’作为边界的回文序列数目,子串s[i+1] [j-1]中的回文序列加上i、j这两个边界字符,构成了dp[x][i][j]的一部分,相比于dp[][i+1][j-1],dp[x][i][j]还要多加2个回文序列,分别为(以x=0为例)a、aa,所以这个2表示的是a、aa
两侧加字符 ch 后,ch 和 chch 丢失了,所以 +2。例如:
aa: a aa
baab: bab baab b bb
bbaabb: bbabb bbaabb bbb bbbb b bb
所以代码如下:
class Solution {
public:
int countPalindromicSubsequences(string S) {
int n = S.size();
int mod = 1000000007;
auto dp_ptr = new vector<vector<vector<int>>>(4, vector<vector<int>>(n, vector<int>(n)));
auto& dp = *dp_ptr;
for (int i = n-1; i >= 0; --i) {
for (int j = i; j < n; ++j) {
for (int k = 0; k < 4; ++k) {
char c = 'a' + k;
if (j == i) {
if (S[i] == c) dp[k][i][j] = 1;
else dp[k][i][j] = 0;
} else { // j > i
if (S[i] != c) dp[k][i][j] = dp[k][i+1][j];
else if (S[j] != c) dp[k][i][j] = dp[k][i][j-1];
else { // S[i] == S[j] == c
if (j == i+1) dp[k][i][j] = 2; // "aa" : {"a", "aa"}
else { // length is > 2
dp[k][i][j] = 2;
for (int m = 0; m < 4; ++m) { // count each one within subwindows [i+1][j-1]
dp[k][i][j] += dp[m][i+1][j-1];
dp[k][i][j] %= mod;
}
}
}
}
}
}
}
int ans = 0;
for (int k = 0; k < 4; ++k) {
ans += dp[k][0][n-1];
ans %= mod;
}
return ans;
}
};