730. Count Different Palindromic Subsequences

87 篇文章 0 订阅
42 篇文章 0 订阅

 

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'}.

先参照discuss里面的memo方法写了一个,有dp方案,以后有时间在写

 

package l730;

import java.util.TreeSet;

class Solution {
    public int countPalindromicSubsequences(String S) {
    	char[] cs = S.toCharArray();
        TreeSet[] ts = new TreeSet[26];
//        Arrays.fill(ts, new TreeSet<Integer>()); // 数组每个treeset是同一个treeset
        for(int i=0; i<26; i++)		ts[i] = new TreeSet<Integer>();
        for(int i=0; i<cs.length; i++)	
        	ts[cs[i]-'a'].add(i);
        
        Integer[][] dp = new Integer[cs.length][cs.length];	// dp值可能默认就是0,所以这里用Integer数组(默认为null) 
        return memo(cs, dp, ts, 0, cs.length-1);
    }

    /*
     * 每次调用一次memo就必须选定一层回文char对应
     * 可以确保不出现重复
     */ 
	private int memo(char[] cs, Integer[][] dp, TreeSet[] ts, int s, int t) {
		if(s > t)				return 0;
		if(dp[s][t] != null)	return dp[s][t];
		
		long ret = 0;
		for(int i=0; i<26; i++) {
			Integer newS = (Integer) ts[i].ceiling(s);
			Integer newT = (Integer) ts[i].floor(t);
			if(newS == null || newT == null || newS > newT)	continue;
			ret ++;		// self alone
			if(newS != newT)	ret++;   // pair
			ret += memo(cs, dp, ts, newS+1, newT-1);
		}
		dp[s][t] = (int) (ret%1000000007);
		return dp[s][t];
	}
}

 

I am not able to pass this question one time but struggle a lot in the basic test cases like "a", "aa", "aaa", "aba", "aabb". Those test cases help my early rough idea to be flawless. The basic idea of DP is easy to understand, I maintain DP[i][j] to record in substring from i to j(included), the number of palindrome without duplicate. Then we consider two cases of the DP equation:

 

when s.charAt(i) != s.charAt(j):
dp[i][j] = dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1];

 

When s.charAt(i) == s.charAt(j):
the situation get much more complex and I fix a lot the wrong answers. I have comment the branches where which kind of test cases are considered.

 

class Solution {
    public int countPalindromicSubsequences(String s) {
        int len = s.length();
        int[][] dp = new int[len][len];

        char[] chs = s.toCharArray();
        for(int i = 0; i < len; i++){
            dp[i][i] = 1;   // Consider the test case "a", "b" "c"...
        }

        for(int distance = 1; distance < len; distance++){
            for(int i = 0; i < len - distance; i++){
                int j = i + distance;
                if(chs[i] == chs[j]){
                    int low = i + 1;
                    int high = j - 1;

              /* Variable low and high here are used to get rid of the duplicate*/

                    while(low <= high && chs[low] != chs[j]){
                        low++;
                    }
                    while(low <= high && chs[high] != chs[j]){
                        high--;
                    }
                    if(low > high){
                        // consider the string from i to j is "a...a" "a...a"... where there is no character 'a' inside the leftmost and rightmost 'a'
                       /* eg:  "aba" while i = 0 and j = 2:  dp[1][1] = 1 records the palindrome{"b"}, 
                         the reason why dp[i + 1][j  - 1] * 2 counted is that we count dp[i + 1][j - 1] one time as {"b"}, 
                         and additional time as {"aba"}. The reason why 2 counted is that we also count {"a", "aa"}. 
                         So totally dp[i][j] record the palindrome: {"a", "b", "aa", "aba"}. 
                         */ 

                        dp[i][j] = dp[i + 1][j - 1] * 2 + 2;  
                    } 
                    else if(low == high){
                        // consider the string from i to j is "a...a...a" where there is only one character 'a' inside the leftmost and rightmost 'a'
                       /* eg:  "aaa" while i = 0 and j = 2: the dp[i + 1][j - 1] records the palindrome {"a"}.  
                         the reason why dp[i + 1][j  - 1] * 2 counted is that we count dp[i + 1][j - 1] one time as {"a"}, 
                         and additional time as {"aaa"}. the reason why 1 counted is that 
                         we also count {"aa"} that the first 'a' come from index i and the second come from index j. So totally dp[i][j] records {"a", "aa", "aaa"}
                        */
                        dp[i][j] = dp[i + 1][j - 1] * 2 + 1;  
                    }
                    else{
                        // consider the string from i to j is "a...a...a... a" where there are at least two character 'a' close to leftmost and rightmost 'a'
                       /* eg: "aacaa" while i = 0 and j = 4: the dp[i + 1][j - 1] records the palindrome {"a",  "c", "aa", "aca"}. 
                          the reason why dp[i + 1][j  - 1] * 2 counted is that we count dp[i + 1][j - 1] one time as {"a",  "c", "aa", "aca"}, 
                          and additional time as {"aaa",  "aca", "aaaa", "aacaa"}.  Now there is duplicate :  {"aca"}, 
                          which is removed by deduce dp[low + 1][high - 1]. So totally dp[i][j] record {"a",  "c", "aa", "aca", "aaa", "aaaa", "aacaa"}
                          */
                        dp[i][j] = dp[i + 1][j - 1] * 2 - dp[low + 1][high - 1]; 
                    }
                }
                else{
                    dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1];  //s.charAt(i) != s.charAt(j)
                }
                dp[i][j] = dp[i][j] < 0 ? dp[i][j] + 1000000007 : dp[i][j] % 1000000007;
            }
        }

        return dp[0][len - 1];
    }
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值