JAVA程序设计:统计不同回文子字符串(LeetCode:730)

 

给定一个字符串 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;
    }
}

猜你喜欢

转载自blog.csdn.net/u011250186/article/details/113504602
今日推荐