Palindrome String
1
2
3
4
5
6
7
8
9
| public boolean isPalindrome(String s) {
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left++) != s.charAt(right--)) {
return false;
}
}
return true;
}
|
Palindrome Number
Palindrome Number
1
2
3
4
5
6
7
8
9
10
11
12
13
| public boolean isPalindrome(int x) {
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
// half revert x
int reverted = 0;
while (x > reverted) {
reverted = reverted * 10 + x % 10;
x /= 10;
}
return x == reverted || x == reverted / 10;
}
|
Construction
Prime Palindrome
A positive integer (in decimal notation) is divisible by 11 iff the difference of the sum of the digits in even-numbered positions and the sum of digits in odd-numbered positions is divisible by 11.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| public int primePalindrome(int N) {
if (N >= 8 && N <= 11) {
return 11;
}
// palindrome with even number of digits is divisible by 11
// x has at most 5 digits
for (int x = 1; x < 100000; x++) {
// builds palindrome with odd number of digits
String s = String.valueOf(x), r = new StringBuilder(s).reverse().toString();
int k = Integer.valueOf(s + r.substring(1));
if (k >= N && isPrime(k)) {
return k;
}
}
return -1;
}
private boolean isPrime(int x) {
if (x < 2 || x % 2 == 0) {
return x == 2;
}
for (int i = 3; i * i <= x; i += 2) {
if (x % i == 0) {
return false;
}
}
return true;
}
|
Super Palindromes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| public int superpalindromesInRange(String left, String right) {
List<Long> palindromes = new ArrayList<>();
for (long i = 1; i <= 9; i++) {
palindromes.add(i);
}
// (10 ^ 18) ^ 0.5 = 10 ^ 9
// the upper limit of left half is 10 ^ (9 / 2 + 1)
for (long i = 1; i < 10000; i++) {
String l = Long.toString(i), r = new StringBuilder(l).reverse().toString();
// even
palindromes.add(Long.parseLong(l + r));
// odd
for (long d = 0; d < 10; d++) {
palindromes.add(Long.parseLong(l + d + r));
}
}
int count = 0;
long low = Long.parseLong(left), high = Long.parseLong(right);
for (long palindrome : palindromes) {
long square = palindrome * palindrome;
if (!isPalindrome(Long.toString(square))) {
continue;
}
if (low <= square && square <= high) {
count++;
}
}
return count;
}
private boolean isPalindrome(String s) {
}
|
Sum of k-Mirror Numbers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| private List<Long> prev = new ArrayList<>(), curr = new ArrayList<>();
private int[] arr = new int[64];
public long kMirror(int k, int n) {
prev.add(0l);
curr.add(0l);
long sum = 0;
// adds single-digit numbers to curr mirror list
for (long i = 1; n > 0 && i < 10; i++) {
curr.add(i);
if (isMirror(i, k)) {
sum += i;
n--;
}
}
return sum + generate(k, n, 10);
}
// generates mirrors to make sure curr mirror list is in order
// firstMul: power of 10
private long generate(int k, int n, long firstMul) {
List<Long> mirrors = new ArrayList<>();
long sum = 0;
for (int i = 0; n > 0 && i < 10; i++) {
for (int j = 0; n > 0 && j < prev.size(); j++) {
long num = firstMul * i + prev.get(j) * 10 + i;
// excludes leading zeros when checking if isMirror
if (i != 0 && isMirror(num, k)) {
sum += num;
n--;
}
// includes leading zerors when generating next level
mirrors.add(num);
}
}
prev = curr;
curr = mirrors;
return sum + (n == 0 ? 0 : generate(k, n, firstMul * 10));
}
private boolean isMirror(long num, int base) {
int j = -1;
while (num != 0) {
arr[++j] = (int)(num % base);
num /= base;
}
int i = 0;
while (i < j) {
if (arr[i++] != arr[j--]) {
return false;
}
}
return true;
}
|
Trie
Palindrome Pairs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
| public List<List<Integer>> palindromePairs(String[] words) {
TrieNode root = new TrieNode();
// builds the trie of reversed words
for (int i = 0; i < words.length; i++) {
String word = words[i];
TrieNode curr = root;
for (int j = word.length() - 1; j >= 0; j--) {
if (isPalindrome(word, 0, j + 1)) {
// e.g. words[0] = "cdeedcba"
// i = 0, j = 5
// root -> curr = "ab", curr -> end = prefix = "cdeedc"
curr.palindromePrefixWordIndices.add(i);
}
char ch = word.charAt(j);
curr.children.putIfAbsent(ch, new TrieNode());
curr = curr.children.get(ch);
}
curr.wordIndex = i;
}
// searches for pairs
List<List<Integer>> list = new ArrayList<>();
for (int i = 0; i < words.length; i++) {
String word = words[i];
TrieNode curr = root;
int j = 0;
while (j < word.length() && curr != null) {
// e.g. word = "abcded", root -> curr = "abc" (original word was the reverse: "cba")
if (curr.wordIndex >= 0 && isPalindrome(word, j, word.length())) {
list.add(Arrays.asList(i, curr.wordIndex));
}
curr = curr.children.get(word.charAt(j++));
}
// curr char == the last char of word
if (curr != null) {
// the pair are the reverse of each other
// e.g. word = "abc", root -> curr = "abc" (original word was the reverse: "cba")
// `curr.wordIndex != i` ensures distinct indices
if (curr.wordIndex >= 0 && curr.wordIndex != i) {
list.add(Arrays.asList(i, curr.wordIndex));
}
// e.g. word = "abc", root -> curr = "abc", curr -> end = "ded"
// (original word was the reverse: "dedcba")
for (int index : curr.palindromePrefixWordIndices) {
list.add(Arrays.asList(i, index));
}
}
}
return list;
}
class TrieNode {
// index of word ending at the current node
// -1 if no word ends here
int wordIndex = -1;
Map<Character, TrieNode> children = new HashMap<>();
List<Integer> palindromePrefixWordIndices = new ArrayList<>();
}
private boolean isPalindrome(String s, int start, int end) {
int i = start, j = end - 1;
while (i < j) {
if (s.charAt(i++) != s.charAt(j--)) {
return false;
}
}
return true;
}
|
Find the Closest Palindrome
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| public String nearestPalindromic(String n) {
int len = n.length();
if (len == 1) {
return String.valueOf(Integer.valueOf(n) - 1);
}
if (n.equals("1" + "0".repeat(len - 2) + "1")) {
return "9".repeat(len - 1);
}
if (n.equals("9".repeat(len))) {
return "1" + "0".repeat(len - 1) + "1";
}
if (n.equals("1" + "0".repeat(len - 1))) {
return "9".repeat(len - 1);
}
String root = n.substring(0, (len + 1) / 2);
String root1 = String.valueOf(Integer.valueOf(root) - 1);
String root2 = String.valueOf(Integer.valueOf(root) + 1);
long nl = Long.valueOf(n);
long p2 = palindrome(root2, len / 2);
long pl = p2;
long diff = Math.abs(p2 - nl);
if (!isPalindrome(n)) {
long p = palindrome(root, len / 2);
if (Math.abs(p - nl) <= diff) {
diff = Math.abs(p - nl);
pl = p;
}
}
long p1 = palindrome(root1, len / 2);
if (Math.abs(p1 - nl) <= diff) {
diff = Math.abs(p1 - nl);
pl = p1;
}
return String.valueOf(pl);
}
private long palindrome(String root, int len) {
return Long.valueOf(root + new StringBuilder(root.substring(0, len)).reverse().toString());
}
|
Greedy
Construct K Palindrome Strings
1
2
3
4
5
6
7
8
9
10
| public boolean canConstruct(String s, int k) {
// bit vector
int odd = 0;
for (char c : s.toCharArray()) {
odd ^= 1 << (c - 'a');
}
// if a bit is 1, it must be the center of a palindrome
return s.length() >= k && Integer.bitCount(odd) <= k;
}
|
Minimum Number of Moves to Make Palindrome
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public int minMovesToMakePalindrome(String s) {
int count = 0;
while (s.length() > 0) {
// finds the occurrence of the end char closest to the start char
int i = s.indexOf(s.charAt(s.length() - 1));
if (i == s.length() - 1) {
// the last char is the center
count += i / 2;
} else {
count += i;
// swaps the found char with the start char
// deletes the two ends of the string
s = s.substring(0, i) + s.substring(i + 1);
}
s = s.substring(0, s.length() - 1);
}
return count;
}
|
Expand Around Center
Palindromic Substring
1
2
3
4
5
6
7
8
9
10
11
12
| public int countSubstrings(String s) {
int n = s.length(), count = 0;
for (int center = 0; center < 2 * n; center++) {
int left = center / 2, right = left + center % 2;
while (left >= 0 && right < n && s.charAt(left) == s.charAt(right)) {
count++;
left--;
right++;
}
}
return count;
}
|
Longest Palindromic Substring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // O(n ^ 2)
public String longestPalindrome(String s) {
String result = "";
int n = s.length(), max = 0;
for (int center = 0; center < 2 * n; center++) {
int left = center / 2, right = left + center % 2;
while (left >= 0 && right < n && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
if (right - left - 1 > max) {
max = right - left - 1;
result = s.substring(left + 1, right);
}
}
return result;
}
|
Maximum Number of Non-overlapping Palindrome Substrings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public int maxPalindromes(String s, int k) {
int n = s.length(), count = 0, start = 0;
for (int center = 0; center < 2 * n; center++) {
int left = center / 2, right = left + center % 2;
while (left >= start && right < n && s.charAt(left) == s.charAt(right)) {
// 435. Non-overlapping Intervals
if (right + 1 - left >= k) {
count++;
start = right + 1;
break;
}
left--;
right++;
}
}
return count;
}
|
Palindrome Partitioning II
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| public int minCut(String s) {
int n = s.length();
// dp[i]: min cut of s.substring(0, i + 1)
int[] dp = new int[n];
// initialization
// partitions into one-char groups
for (int i = 0; i < n; i++) {
dp[i] = i;
}
for (int center = 0; center <= 2 * n - 1; center++) {
int left = center / 2, right = left + center % 2;
while (left >= 0 && right < n && s.charAt(left) == s.charAt(right)) {
// s.substring(0, left) + current palindrome == s.substring(0, right + 1);
dp[right] = Math.min(dp[right], left == 0 ? 0 : dp[left - 1] + 1);
left--;
right++;
}
}
return dp[n - 1];
}
|
Manacher’s Algorithm
Manacher’s algorithm: find all palindromic substrings in O(n)
.
Longest Palindromic Substring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| // O(n)
public String longestPalindrome(String s) {
// string ms = s with a bogus character (eg. '#') inserted between each character
// (including outer boundaries)
StringJoiner sj = new StringJoiner("#", "#", "#");
for (char ch : s.toCharArray()) {
sj.add(String.valueOf(ch));
}
String ms = sj.toString();
int n = ms.length();
// radii[i]: radius of the longest palindrome centered at ms[i]
int[] radii = new int[n];
// center and right bound of the longest palindromic substring so far
int c = -1, r = -1;
int max = Integer.MIN_VALUE, pos = -1;
for (int i = 0; i < n; i++) {
// 2 * c - i is the mirrored center of i
// radius is bounded by the outer palindrome
// or it's a candidate
radii[i] = i < r ? Math.min(radii[2 * c - i], r - i) : 1;
// determines the longest palindrome in [center - radius, center + radius]
while (i - radii[i] >= 0 && i + radii[i] < n && ms.charAt(i + radii[i]) == ms.charAt(i - radii[i])) {
radii[i]++;
}
// updates r and c if current right bound is beyond r
if (i + radii[i] > r) {
r = i + radii[i];
c = i;
}
// tracks the max and its position
if (radii[i] > max) {
max = radii[i];
pos = i;
}
}
StringBuilder sb = new StringBuilder();
for (int i = pos - radii[pos] + 1; i <= pos + radii[pos] - 1; i++) {
if (ms.charAt(i) != '#') {
sb.append(ms.charAt(i));
}
}
return sb.toString();
}
|
Maximum Product of the Length of Two Palindromic Substrings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| public long maxProduct(String s) {
// Manacher's Algorithm (https://cp-algorithms.com/string/manacher.html)
int n = s.length();
int[] radii = new int[n];
for (int i = 0, l = 0, r = -1; i < n; i++) {
int k = (i > r) ? 1 : Math.min(radii[l + r - i], r - i + 1);
while (0 <= i - k && i + k < n && s.charAt(i - k) == s.charAt(i + k)) {
k++;
}
radii[i] = k--;
if (i + k > r) {
l = i - k;
r = i + k;
}
}
// r[i]: max length of palindrome whose left is i
int[] r = new int[n];
Queue<Integer> q1 = new LinkedList<>(), q2 = new LinkedList<>();;
// from right to left
// finds the max palindrome whose left bound >= i
for (int i = n - 1; i > 0; i--) {
// left bound = center - radius
// loops until the current index is covered by the palindrome in queue front
while (!q1.isEmpty() && q1.peek() - radii[q1.peek()] >= i) {
q1.poll();
}
r[i] = 1 + (q1.isEmpty() ? 0 : (q1.peek() - i) * 2);
q1.offer(i);
}
// l: max length of palindrome whose right is i
long l = 0, product = 0;
// from left to right
// finds the max palindrome whose right bound <= i
for (int i = 0; i < n - 1; i++) {
// right bound = center + radius
// loops until the current index is covered by the palindrome in queue front
while (!q2.isEmpty() && q2.peek() + radii[q2.peek()] <= i) {
q2.poll();
}
l = Math.max(l, 1 + (q2.isEmpty() ? 0 : (i - q2.peek()) * 2));
product = Math.max(product, l * r[i + 1]);
q2.offer(i);
}
return product;
}
|
Dynamic Programming
Iteration pattern:
1
2
| for (int i = n - 1; i >= 0; i--) {
for (int j = i; j < n; j++) {
|
Palindrome Partitioning IV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| public boolean checkPartitioning(String s) {
int n = s.length();
// dp[i][j]: s.substring(i, j + 1)
boolean[][] dp = new boolean[n][n];
for (int i = n - 1; i >= 0; i--) {
dp[i][i] == true;
for (int j = i + 1; j < n; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = (j == i + 1 ? true : dp[i + 1][j - 1]);
} else {
dp[i][j] = false;
}
}
}
for (int i = 1; i < n - 1; i++) {
for (int j = i; j < n - 1; j++) {
if (dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1]) {
return true;
}
}
}
return false;
}
|
Longest Palindromic Subsequence
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public int longestPalindromeSubseq(String s) {
int n = s.length();
// dp[i][j]: s.substring(i, j + 1)
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < n; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
|
Palindrome Removal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| public int minimumMoves(int[] arr) {
int n = arr.length;
// dp[i][j]: minimum number of moves for arr[i...j]
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
if (i < n - 1) {
dp[i][i + 1] = arr[i] == arr[i + 1] ? 1 : 2;
}
for (int j = i + 2; j < n; j++) {
dp[i][j] = Integer.MAX_VALUE;
if (arr[i] == arr[j]) {
// arr[i] and arr[j] can be removed together with the last move of arr[(i + 1)...(j - 1)]
dp[i][j] = dp[i + 1][j - 1];
}
// or, splits arr[i...j] in two parts
for (int k = i; k < j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
}
}
return dp[0][n - 1];
}
|
Minimum Changes to Make K Semi-palindromes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| int minimumChanges(string s, int k) {
int n = s.size();
// Find all d's for each length.
// A semi-palindrome has at least 2 characters.
vector<vector<int>> divisors(n + 1, vector<int>(1, 1));
for (int d = 2; d < n; d++) {
for (int v = 2 * d; v <= n; v += d) {
divisors[v].push_back(d);
}
}
// [i, j, d]: s.substr(i, j - i + 1)
// semi[i][j][0] = min(semi[i][j][d]), where d > 0
vector<vector<vector<int>>> semi(n, vector<vector<int>>(n, vector<int>(n)));
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
semi[i][j][0] = n;
for (int d : divisors[j - i + 1]) {
if (i + d < n && j - d >= 0) {
semi[i][j][d] = semi[i + d][j - d][d];
}
for (int m = 0; m < d; m++) {
semi[i][j][d] += s[i + m] != s[j - d + 1 + m];
}
semi[i][j][0] = min(semi[i][j][0], semi[i][j][d]);
}
}
}
vector<vector<int>> dp(n + 1, vector<int>(k + 1));
// m = 1
for (int i = 1; i < n; i++) {
dp[i + 1][1] = semi[0][i][0];
}
for (int m = 2; m <= k; m++) {
for (int i = 2 * m; i <= n; i++) {
dp[i][m] = n;
for (int j = 2 * (m - 1); j < i - 1; j++) {
dp[i][m] = min(dp[i][m], dp[j][m - 1] + semi[j][i - 1][0]);
}
}
}
return dp[n][k];
}
|
Maximum Product of the Length of Two Palindromic Subsequences
1
2
3
4
5
6
7
8
9
10
11
12
| public int maxProduct(String s) {
int n = s.length(), max = 0;
// iterates all masks
for (int i = 0; i < (1 << n); i++) {
StringBuilder ones = new StringBuilder(), zeros = new StringBuilder();
for (int j = 0; j < n; j++) {
((i & (1 << j)) == 0 ? zeros : ones).append(s.charAt(j));
}
max = Math.max(max, longestPalindromeSubseq(ones) * longestPalindromeSubseq(zeros));
}
return max;
}
|
This problem can be solved by backtracking, too. It reflects the close connection between bitmask and backtracking.
Palindrome Partitioning III
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| public int palindromePartition(String s, int k) {
int n = s.length();
// dp1[i][j]: minimum steps to make s.substring(i, j + 1) palindrome
int[][] dp1 = new int[n][n];
for (int i = n - 1; i >= 0; i--) {
// dp1[i][i] == 0
for (int j = i + 1; j < n; j++) {
dp1[i][j] = dp1[i + 1][j - 1] + (s.charAt(i) == s.charAt(j) ? 0 : 1);
}
}
// dp2[i][m]: s.substring(0, i), m chunks
int[][] dp2 = new int[n + 1][k + 1];
for (int i = 1; i <= n; i++) {
dp2[i][1] = dp1[0][i - 1];
}
for (int m = 2; m <= k; m++) {
// dp[m][m] = 0
for (int i = m + 1; i <= n; i++) {
dp2[i][m] = Integer.MAX_VALUE;
for (int j = m - 1; j < i; j++) {
dp2[i][m] = Math.min(dp2[i][m], dp2[j][m - 1] + dp1[j][i - 1]);
}
}
}
return dp2[n][k];
}
|
Maximize Palindrome Length From Subsequences
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| public int longestPalindrome(String word1, String word2) {
String s = word1 + word2;
int n = s.length(), n1 = word1.length();
// dp[i][j]: s.substring(i, j + 1)
int[][] dp = new int[n][n];
int max = 0;
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < n; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
if (i < n1 && j >= n1) {
max = Math.max(max, dp[i][j]);
}
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return max;
}
|
Count Different Palindromic Subsequences
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| private static final int MOD = (int)1e9 + 7, NUM = 4;
public int countPalindromicSubsequences(String S) {
int n = S.length();
int[] prev = new int[n + 1], next = new int[n + 1];
int[] index = new int[NUM];
Arrays.fill(index, -1);
for (int i = 0; i < n; i++) {
prev[i] = index[S.charAt(i) - 'a'];
index[S.charAt(i) - 'a'] = i;
}
Arrays.fill(index, n);
for (int i = n - 1; i >= 0; i--) {
next[i] = index[S.charAt(i) - 'a'];
index[S.charAt(i) - 'a'] = i;
}
int[][] dp = new int[n][n];
// "a"
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
for (int len = 1; len < n; len++) {
for (int i = 0, j = i + len; j < n; i++, j++) {
if (S.charAt(i) != S.charAt(j)) {
dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1];
} else {
// [i, j]
int low = next[i], high = prev[j];
dp[i][j] = dp[i + 1][j - 1] * 2; // w/ and w/o S(i) & S(j)
if (low == high) { // a...a...a, only one char 'a' inside
dp[i][j]++; // +{"aa"}
} else if (low > high) { // a...a, no char 'a' inside
dp[i][j] += 2; // +{"a", "aa"}
} else { // a...a...a...a
dp[i][j] -= dp[low + 1][high - 1];
}
}
dp[i][j] = Math.floorMod(dp[i][j], MOD);
}
}
return dp[0][n - 1];
}
|
Count Palindromic Subsequences
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| // @param d: iteration direction.
auto precompute(string s, int d = 1) {
int n = s.length();
vector<int> freqs(10);
freqs[s[d > 0 ? 0 : n - 1] - '0'] = 1;
// dp[i][j][k]: occurrences of ordered pair ('j', 'k') in s[0...i] (prefix) or s[i...(n - 1)] (suffix).
vector<array<array<int, 10>, 10>> dp(n);
for (int i = d > 0 ? 1 : n - 2; i >= 0 && i < n; i += d) {
int digit = s[i] - '0';
for (int j = 0; j < 10; j++) {
for (int k = 0; k < 10; k++) {
dp[i][j][k] = dp[i - d][j][k] + (k == digit ? freqs[j] : 0);
}
}
freqs[digit]++;
}
return dp;
}
public:
int countPalindromes(string s) {
// Prefix and suffix
auto pDp = precompute(s), sDp = precompute(s, -1);
const int mod = 1e9 + 7;
int cnt = 0, n = s.length();
for (int i = 2; i < n - 2; i++) {
for (int j = 0; j < 10; j++) {
for (int k = 0; k < 10; k++) {
cnt = (cnt + static_cast<long long>(pDp[i - 1][j][k]) * sDp[i + 1][j][k]) % mod;
}
}
}
return cnt;
}
|
The dp
array in this solution is not a prefix sum array of counts of ordered pairs. e.g. s = "103301"
, pDp[5][0][1] = 2
and pDp[4][0][1] = 0
, we cannot say between index 4 and 5, s
contains pDp[5][0][1] - pDp[4][0][1] = 2
ordered pairs.