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.
1 class Solution { 2 public int countPalindromicSubsequences(String S) { 3 Map<String, Set<String>> _seqMap = new HashMap<>(); 4 return findPalindromesHelper(S, _seqMap).size(); 5 } 6 7 private Set<String> findPalindromesHelper(String s, Map<String, Set<String>> _seqMap) { 8 Set<String> result = _seqMap.get(s); 9 if (result != null) { 10 return result; 11 } 12 int len = s.length(); 13 result = new HashSet<String>(); 14 if (len < 1) { 15 return result; 16 } 17 if (len == 1) { 18 result.add(s); 19 return result; 20 } 21 result.addAll(findPalindromesHelper(s.substring(0, len - 1), _seqMap)); 22 result.addAll(findPalindromesHelper(s.substring(1, len), _seqMap)); 23 if (s.charAt(0) == s.charAt(len - 1)) { 24 Set<String> subSet = findPalindromesHelper(s.substring(1, len - 1), _seqMap); 25 String head = s.substring(0, 1); 26 for (String s1 : subSet) { 27 result.add(head + s1 + head); 28 } 29 result.add(head + head); 30 } 31 _seqMap.put(s, result); 32 return result; 33 } 34 }
1 class Solution { 2 public int countPalindromicSubsequences(String s) { 3 int len = s.length(); 4 int[][] dp = new int[len][len]; 5 6 char[] chs = s.toCharArray(); 7 for (int i = 0; i < len; i++) { 8 dp[i][i] = 1; // Consider the test case "a", "b" "c"... 9 } 10 11 for (int distance = 1; distance < len; distance++) { 12 for (int i = 0; i < len - distance; i++) { 13 int j = i + distance; 14 if (chs[i] == chs[j]) { 15 int low = i + 1, high = j - 1; 16 while (low <= high && chs[low] != chs[j]) { 17 low++; 18 } 19 while (low <= high && chs[high] != chs[j]) { 20 high--; 21 } 22 if (low > high) { 23 /* 24 * consider the string from i to j is "a...a" "a...a"... where there is no 25 * character 'a' inside the leftmost and rightmost 'a' 26 * 27 * eg: "aba" while i = 0 and j = 2: dp[1][1] = 1 records the palindrome{"b"}, 28 * the reason why dp[i + 1][j - 1] * 2 counted is that we count dp[i + 1][j - 1] 29 * one time as {"b"}, and additional time as {"aba"}. The reason why 2 counted 30 * is that we also count {"a", "aa"}. So totally dp[i][j] record the palindrome: 31 * {"a", "b", "aa", "aba"}. 32 */ 33 dp[i][j] = dp[i + 1][j - 1] * 2 + 2; 34 } else if (low == high) { 35 /* 36 * consider the string from i to j is "a...a...a" where there is only one 37 * character 'a' inside the leftmost and rightmost 'a' 38 * 39 * eg: "aaa" while i = 0 and j = 2: the dp[i + 1][j - 1] records the palindrome 40 * {"a"}. the reason why dp[i + 1][j - 1] * 2 counted is that we count dp[i + 41 * 1][j - 1] one time as {"a"}, and additional time as {"aaa"}. the reason why 1 42 * counted is that we also count {"aa"} that the first 'a' come from index i and 43 * the second come from index j. So totally dp[i][j] records {"a", "aa", "aaa"} 44 */ 45 dp[i][j] = dp[i + 1][j - 1] * 2 + 1; 46 } else { 47 /* 48 * consider the string from i to j is "a...a...a... a" where there are at least 49 * two character 'a' close to leftmost and rightmost 'a' 50 * 51 * eg: "aacaa" while i = 0 and j = 4: the dp[i + 1][j - 1] records the 52 * palindrome {"a", "c", "aa", "aca"}. the reason why dp[i + 1][j - 1] * 2 53 * counted is that we count dp[i + 1][j - 1] one time as {"a", "c", "aa", 54 * "aca"}, and additional time as {"aaa", "aca", "aaaa", "aacaa"}. Now there is 55 * duplicate : {"aca"}, which is removed by deduce dp[low + 1][high - 1]. So 56 * totally dp[i][j] record {"a", "c", "aa", "aca", "aaa", "aaaa", "aacaa"} 57 */ 58 dp[i][j] = dp[i + 1][j - 1] * 2 - dp[low + 1][high - 1]; 59 } 60 } else { 61 dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1]; // s.charAt(i) != s.charAt(j) 62 } 63 dp[i][j] = dp[i][j] < 0 ? dp[i][j] + 1000000007 : dp[i][j] % 1000000007; 64 } 65 } 66 return dp[0][len - 1]; 67 } 68 }