Question
Given a string S, find the number of different non-empty palindromic subsequences in S, and return that number modulo 10^9 + 7.A subsequence of a string S is obtained by deleting 0 or more characters from S.
A sequence is palindromic if it is equal to the sequence reversed.
Two sequences A_1, A_2, ... and B_1, B_2, ... are different if there is some i for which A_i != B_i.
Example 1:
Input:
S = 'bccb'
Output: 6
Explanation:
The 6 different non-empty palindromic subsequences are 'b', 'c', 'bb', 'cc', 'bcb', 'bccb'.
Note that 'bcb' is counted only once, even though it occurs twice.
Example 2:
Input:
S = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba'
Output: 104860361
Explanation:
There are 3104860382 different non-empty palindromic subsequences, which is 104860361 modulo 10^9 + 7.
Note:
- The length of S will be in the range [1, 1000].
- Each character S[i] will be in the set {'a', 'b', 'c', 'd'}.
Analysis
这个题目看起来很复杂,但是用动态规划求解其实很简单。本文的方法参考了花花酱在youtube上的视频。
具体思路是,设dp[i,j]表示S[i]到S[j]之间这段字符串中不重复的回文子串的数量。那么在求dp[i,j]的时候,根据S[i]和S[j]是否相同,进行分类讨论。主要思想是用子问题组合解决新问题,同时去掉重复计数的部分。
若S[i]!=S[j],则 dp[i,j] = dp[i, j-1] +dp[i+1,j] - dp[i+1,j-1]
若S[i]=S[j],需要根据S[i+1]-S[j-1]之间S[i]的个数进一步分类讨论,分为包含零个、一个以及两个或更多这三种情况:
S[i+1]-S[j-1]中间没有S[i]: dp[i,j] = dp[i+1, j-1] * 2 + 2
S[i+1]-S[j-1]中间有一个S[i]: dp[i,j] = dp[i+1, j-1] * 2 + 1
S[i+1]-S[j-1]中间有两个S[i],其中第一个S[i]出现在ii位置,最后一个出现在jj位置: dp[i,j] = dp[i+1, j-1] * 2 - dp[ii+1, jj-1]
总结一下:
起始值为:dp[i,i]=1; dp[i,j] = 0 (i > j);
只要理解了其中的思路,实现起来就非常容易了。
Solution
class Solution {
public:
int countPalindromicSubsequences(string S) {
int n = S.length();
long int mod = 1000000007;
vector<vector<long int>>dp(n, vector<long int>(n, 0));
for(int i = 0; i < n; ++i){
dp[i][i] = 1;
}
for(int len = 1; len < n; ++len){
for(int i = 0; i < n - len; ++i){
int j = i + len;
if(S[i] == S[j]){
int ii = i + 1;
while(S[ii] != S[i])
ii++;
int jj = j - 1;
while(S[jj] != S[j])
jj--;
if(ii == jj)
dp[i][j] = dp[i + 1][j - 1] * 2 + 1;
else if(ii > jj)
dp[i][j] = dp[i + 1][j - 1] * 2 + 2;
else
dp[i][j] = dp[i + 1][j - 1] * 2 - dp[ii + 1][jj - 1];
}
else{
dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1];
}
dp[i][j] = (dp[i][j] + mod) % mod;
}
}
return dp[0][n - 1];
}
};