给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模。
通过从 S 中删除 0 个或多个字符来获得子字符序列。
如果一个字符序列与它反转后的字符序列一致,那么它是回文字符序列。
如果对于某个 i,A_i != B_i,那么 A_1, A_2, ... 和 B_1, B_2, ... 这两个字符序列是不同的。
示例 1:
输入:
S = 'bccb'
输出:6
解释:
6 个不同的非空回文子字符序列分别为:'b', 'c', 'bb', 'cc', 'bcb', 'bccb'。
注意:'bcb' 虽然出现两次但仅计数一次。
示例 2:
输入:
S = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba'
输出:104860361
解释:
共有 3104860382 个不同的非空回文子字符序列,对 10^9 + 7 取模为 104860361。
提示:
字符串 S 的长度将在[1, 1000]范围内。
每个字符 S[i] 将会是集合 {'a', 'b', 'c', 'd'} 中的某一个。
方法一:动态规划(三维数组实现)
我们设dp[x][i][j]
为子串 S[i...j]
拥有不同回文子字符串的答案,其中x为0到3表示子序列以四种字符的某一种结尾的方案数。
class Solution {
private int mod=1000000007;
public int countPalindromicSubsequences(String S) {
int ans=0;
int len=S.length();
int[][][] dp=new int[4][len][len];
for(int i=len-1;i>=0;i--)
for(int j=i;j<len;j++)
for(int k=0;k<4;k++) {
char c=(char)('a'+k);
if(i==j) {
if(S.charAt(i)==c) dp[k][i][j]=1;
else dp[k][i][j]=0;
}
else {
if(S.charAt(i)!=c) dp[k][i][j]=dp[k][i+1][j];
else if(S.charAt(j)!=c) dp[k][i][j]=dp[k][i][j-1];
else {
if(j==i+1) dp[k][i][j]=2;
else {
dp[k][i][j]=2;
for(int m=0;m<4;m++) {
dp[k][i][j]+=dp[m][i+1][j-1];
dp[k][i][j]%=mod;
}
}
}
}
}
for(int i=0;i<4;i++)
ans=(ans+dp[i][0][len-1])%mod;
return ans;
}
}
方法二:动态规划(二维数组实现)
在上一个方法的基础上进行空间的简化,因为我们完全没有必要多开一维存储回文串左右端点的字符,因为回文串的情况无非是单独的a,b,c,d,或者a....a,b....b,c....c,d....d这样的形式,我们直接计数就好啦。
class Solution {
int[] p,last;
int[][] memo,pre,nxt;
private int mod=1000000007;
public int countPalindromicSubsequences(String S) {
int len=S.length();
last=new int[4];
p=new int[len];
pre=new int[len][4];
nxt=new int[len][4];
memo=new int[len][len];
for(int i=0;i<len;i++) {
for(int j=0;j<4;j++) {
pre[i][j]=-1;
nxt[i][j]=-1;
}
p[i]=(int)(S.charAt(i)-'a');
}
for(int i=0;i<4;i++) last[i]=-1;
for(int i=0;i<len;i++) {
last[p[i]]=i;
for(int j=0;j<4;j++)
pre[i][j]=last[j];
}
for(int i=0;i<4;i++) last[i]=-1;
for(int i=len-1;i>=0;i--) {
last[p[i]]=i;
for(int j=0;j<4;j++)
nxt[i][j]=last[j];
}
return dp(0,len-1)-1;
}
private int dp(int l,int r) {
if(memo[l][r]>0) return memo[l][r];
int ans=1;
if(l<=r) {
for(int k=0;k<4;k++) {
int p1=nxt[l][k];
int p2=pre[r][k];
if(l<=p1 && p1<=r) ans++;
if(-1<p1 && p1<p2) ans+=dp(p1+1,p2-1);
if(ans>=mod) ans-=mod;
}
}
memo[l][r]=ans;
return ans;
}
}