1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { public int longestPalindromeSubseq(String s) { Integer[][] memo = new Integer[s.length()][s.length()]; return helper(s.toCharArray(), 0, s.length() - 1, memo); }
private int helper(char[] sArray, int left, int right, Integer[][] memo) { if (left > right) { return 0; } if (left == right) { return 1; } if (memo[left][right] != null) { return memo[left][right]; } if (sArray[left] == sArray[right]) { return memo[left][right] = helper(sArray, left + 1, right - 1, memo) + 2; } return memo[left][right] = Math.max(helper(sArray, left + 1, right, memo), helper(sArray, left, right - 1, memo)); } }
|
上面是recursion + memoization的解法. 这个比较容易想出来. 思路就是两个指针在两头. 如果它们指向的char相同, 那么我们就需要求[left + 1, right - 1]框起来的LPS即可, 求到的结果加上2就ok了. 如果二者不同, 我们要么是不要left指向的char, 要么是不要right指向的char. 那为什么不能两个都不要呢? 因为这种情况会被包含在我们之前的操作中. 因为left和right都不要构成的substring是和只不要left和只不要right构成的substring的substring. 因此该情况是被包含的. 递归函数求的就是某个substring中最长的LPS.
时间复杂度: O(n^2) left可能的数字有[0, n - 1]. 如果left == 0, right有n - 1种选择, 如果left == 1, right有n - 2种选择, 以此类推. 因此是O(n^2)的情况. 我们用memo存, 每种情况只会经历一次, 因此是O(N^2)
空间复杂度: O(n^2) 因为用了memo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public int longestPalindromeSubseq(String s) { int[][] memo = new int[s.length()][s.length()]; char[] sArray = s.toCharArray(); for (int i = memo.length - 1; i >= 0; i--) { memo[i][i] = 1; for (int j = i + 1; j < memo[0].length; j++) { if (sArray[i] == sArray[j]) { memo[i][j] = memo[i + 1][j - 1] + 2; } else { memo[i][j] = Math.max(memo[i + 1][j], memo[i][j - 1]); } } } return memo[0][memo[0].length - 1]; } }
|
根据上面的recursion写出的dp答案. 此时的dp依赖左侧或者下侧的答案. 对角线的值是1, 对角线左侧的值为0. 因此我们一开始遇到对角线的时候单独处理.
时间复杂度: O(N^2)
空间复杂度: O(N^2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution { public int longestPalindromeSubseq(String s) { int[] memo = new int[s.length()]; char[] sArray = s.toCharArray(); for (int i = memo.length - 1; i >= 0; i--) { memo[i] = 1; int currDiag = 0; for (int j = i + 1; j < memo.length; j++) { int nextDiag = memo[j]; if (sArray[i] == sArray[j]) { memo[j] = currDiag + 2; } else { memo[j] = Math.max(memo[j], memo[j - 1]); } currDiag = nextDiag; } } return memo[memo.length - 1]; } }
|
我们需要的左下角, 因此我们用一个变量记录这个左下角. 同时在我们更新当前的值之前, 这个位置装的是下一个位置需要的左下角的值, 因此我们也暂存一下. 这样1D dp就成了.
时间复杂度: O(n^2)
空间复杂度: O(n)