Reputation: 21
Here is longest palindrome solution I have written using dynamic programming with memoization. It passes several of the test cases (46/142), but the solution is not at 100% on Leetcode. Time limit is exceeding (TLE) for very long inputs. How to make it work for long inputs as well?
public String longestPalindrome(String s) {
Map<List<Integer>, String> memo = new HashMap<>();
return longest(s, 0, s.length()-1, memo);
}
public static String getMaxString(String s1, String s2) {
return (s1.length() > s2.length() ? s1 : s2);
}
public String longest(String s, int i, int j, Map<List<Integer>, String> memo) {
// Invalid indices.
if (i > j) {
return "";
}
List<Integer> keyPair = List.of(i, j);
if (i == j) {
return s.substring(i, j+1);
}
// Return palindrome if already evaluated.
if(memo.containsKey(keyPair)) {
return memo.get(keyPair);
}
// Check if the inner string is also a palindrome and update max accordingly.
String max = longest(s, i+1, j-1, memo);
if ((max.length() == (j-1) - (i+1) + 1) &&
(s.charAt(i) == s.charAt(j)))
{
max = s.substring(i,j+1);
}
// Get max of all possible palindromes within [i,j].
max = getMaxString(max, getMaxString(longest(s, i+1, j, memo),
longest(s, i, j-1, memo)));
memo.put(keyPair, max);
return memo.get(keyPair);
}
I have seen various other algorithms for this problem, but, what I am specifically looking for is to understand how to make my solution work for all inputs (or) the gaps in my solution.
Upvotes: -5
Views: 104
Reputation: 89314
First, your solution is O(N^3)
: you have O(N^2)
states (the possible pairs of indexes corresponding to substrings) and you potentially call substring
in each recursive call, which is O(N)
.
Instead of returning the actual string from the recursive longest
method, simply return a pair of integers to represent the substring. This representation allows constant time manipulation and reduces the overall time complexity to O(N^2)
; you only need to call substring
to get the result at the very end.
Furthermore, while HashMap
provides constant time operations on average, it has a high constant factor and can still get quite slow with a large number of keys. In this case, you already know the possible key values (indexes are all between 0
and s.length() - 1
), so it is more efficient to use a 2d array to store the memoized results.
public String longestPalindrome(String s) {
var memo = new int[s.length()][s.length()][];
var ret = longest(s, 0, s.length()-1, memo);
return s.substring(ret[0], ret[1]);
}
public static int[] getMaxString(int[] s1, int[] s2) {
return s1[1] - s1[0] > s2[1] - s2[0] ? s1 : s2;
}
public int[] longest(String s, int i, int j, int[][][] memo) {
if (i > j) {
return new int[2];
}
if (i == j) {
return new int[]{i, j+1};
}
if (memo[i][j] != null) return memo[i][j];
var max = longest(s, i+1, j-1, memo);
if ((max[1] - max[0] == (j-1) - (i+1) + 1) &&
(s.charAt(i) == s.charAt(j))) {
max = new int[]{i, j+1};
}
max = getMaxString(max, getMaxString(longest(s, i+1, j, memo),
longest(s, i, j-1, memo)));
return memo[i][j] = max;
}
There is also a linear time solution to the longest palindromic substring problem: Manacher's Algorithm.
Upvotes: 3