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
| class Solution { private Boolean[][] memo;
public boolean isInterleave(String s1, String s2, String s3) { if (s3.length() != s1.length() + s2.length()) { return false; } memo = new Boolean[s1.length() + 1][s2.length() + 1]; return helper(s1, s2, s3, 0, 0); }
private boolean helper(String s1, String s2, String s3, int pos1, int pos2) { if (memo[pos1][pos2] != null) { return memo[pos1][pos2]; } if (pos1 == s1.length()) { return memo[pos1][pos2] = s2.substring(pos2).equals(s3.substring(s3.length() - s2.length() + pos2)); } if (pos2 == s2.length()) { return memo[pos1][pos2] = s1.substring(pos1).equals(s3.substring(s3.length() - s1.length() + pos1)); } if (s1.charAt(pos1) == s3.charAt(pos1 + pos2)) { if (helper(s1, s2, s3, pos1 + 1, pos2)) { return memo[pos1][pos2] = true; } } if (s2.charAt(pos2) == s3.charAt(pos1 + pos2)) { if (helper(s1, s2, s3, pos1, pos2 + 1)) { return memo[pos1][pos2] = true; } } return memo[pos1][pos2] = false; }
}
|
自顶向下recursion + memoization. 光recursion的话会TLE.
时间复杂度: O(m * n) 一共有m * n个subproblems
空间复杂度: O(m * n) 用了memo
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 boolean isInterleave(String s1, String s2, String s3) { if (s3.length() != s1.length() + s2.length()) { return false; } boolean memo = new boolean[s1.length() + 1][s2.length() + 1]; memo[s1.length()][s2.length()] = true; for (int i = s2.length() - 1; i >= 0; i--) { memo[s1.length()][i] = memo[s1.length()][i + 1] & (s2.charAt(i) == s3.charAt(s1.length() + i)); } for (int i = s1.length() - 1; i >= 0; i--) { memo[i][s2.length()] = memo[i + 1][s2.length()] & (s1.charAt(i) == s3.charAt(s2.length() + i)); } for (int i = s1.length() - 1; i >= 0; i--) { for (int j = s2.length() - 1; j >= 0; j--) { memo[i][j] = (s1.charAt(i) == s3.charAt(i + j) && memo[i + 1][j]) || (s2.charAt(j) == s3.charAt(i + j) && memo[i][j + 1]); } } return memo[0][0]; } }
|
DP的解法. 其实就是根据recursion的逻辑写的dp转移方程. helper中出现调用helper的地方就代表着依赖着某个状态. 于是按照这个思路, 写dp也就容易了. 需要注意的是当pos1 == s1.length()的时候(对应dp中最下面一行)或pos2 == s2.length()的时候, 我们会直接比较s2剩下的char是否和s3剩下的char一样或者s1剩下的char是否和s3一样. 其实也可以换一种思路, 那就是s2或者s1不再贡献char, 于是我们继续看s1或者s2当前的char是否和s3对应的char相同, 如果相同, 那就看s1或者s2下一位往后的char是否和s3往后的char相同. 等于是还是按照正常的逻辑去走, 而不是直接省事去substring. 这样边界的状态我们就可以先得到了.
时间复杂度: O(m * n) m和n是s1 + s2的长度.
空间复杂度: O(m * n) 用了memo这个二维数组.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution { public boolean isInterleave(String s1, String s2, String s3) { if (s3.length() != s1.length() + s2.length()) { return false; } memo = new boolean[s2.length() + 1]; memo[s2.length()] = true; for (int i = s2.length() - 1; i >= 0; i--) { memo[i] = memo[i + 1] & (s2.charAt(i) == s3.charAt(s1.length() + i)); } for (int i = s1.length() - 1; i >= 0; i--) { memo[s2.length()] = memo[s2.length()] && s1.charAt(i) == s3.charAt(s2.length() + i); for (int j = s2.length() - 1; j >= 0; j--) { memo[j] = (s1.charAt(i) == s3.charAt(i + j) && memo[j]) || (s2.charAt(j) == s3.charAt(i + j) && memo[j + 1]); } } return memo[0]; } }
|
1D Dp array. 因为只依赖我们的右侧和下侧的状态, 因此可以缩减为1维数组.
需要注意的是我们在进入inner loop前要手动算一下memo[s2.length()]是多少, 因为它的状态只依赖于下面的dp, 它右侧没有dp. 如果不写这一行, 有个test case会过不了, 那就是如果s2的长度为0, s1的长度为1的情况.
时间复杂度: O(m * n)
空间复杂度: O(min(m * n))