问题
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
Example 1:
Input: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
Output: true
Example 2:
Input: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
Output: false
分析
动态规划解的字符串问题
很容易想到
暴力动态规划:
以Example1为例,s1的第一个字符与s3的第一个字符匹配;如果满足s3.substring(1)是s1.substring(1)与s2的交错字符串,则s3是s1和s2的交错字符串。
这里注意:优先匹配s1的字符还是优先匹配s2的字符可能会导致最终匹配结果的成功或失败;所以匹配完s1的首个字符之后(若此次匹配失败),应该匹配s2的首个字符与s3的首个字符。
举个栗子:
s1=“c”,s2=“ca”,s3=“cac”
如果先匹配s1的第一个字符,最终匹配结果为false;
这是匹配s2的第一个字符与s3的第一个字符,最终匹配结果为true。
时间复杂度:O(2^(m+n)),不出意外的超时了
带备忘录的动态规划:(来自官方解答)
使用二维备忘录矩阵mem[i][j],记录s1.substring(i),s2.substring(j)是否是s3.substring(k)的交错字符串。(这里,满足s1.length()-i+s2.length()-j=s3.length()-k)
备忘录初始化为-1;memo[i][j]=1是,表示剩余字符匹配成功;memo[i][j]=0时,表示匹配失败。
优点在于:减少重复递归。
时间复杂度:O(2^(m+n)),意料之外的也超时了,毕竟还是这个级别的复杂度。
一个由于太困和纯粹不想学习并只想听歌的下午啊 把剩下两个方法糊弄一下
二维数组的动态规划:(来自官方解答,too)
本题存在这样一个规则:
对于字符串s1,s2和s3,分别记三者当前的指针序号为i,j,k。(位置序号从0开始)
设一个二维数组dp[i][j],表示s1的0—i位置的子串和s2的0—j位置的子串与s3的0—i+j+1位置的子串是否可以交错生成,如果为true,表示可以;vice visa。
规则是:
- 如果s3的i+j+1位置的字符既不与s1的i位置的字符相同,又不与s2的j位置的字符相同,则上面那句话不成立(即不可有子串交错生成),dp[i][j]=false
- 如果s1的i位置与s3的i+j+1位置字符相同,则为了使s1的0—i位置和s2的0—j位置能交错生成字符串s3的0—i+j+1位置,则要求s1的0—i-1位置和s2的0—j位置能交错生成s3的0—i+j位置,即dp[i-1][j]=true;当前以上两条件都满足时,dp[i][j]=true;vice visa.
- 如果s2的j位置与s3的i+j+1位置字符相同,则要判断dp[i][j-1]
(其中第二个和第三条件只要满足一个,则该位置的取值都为true)
(啊我可能没说清楚……有点不清楚但又感觉是这样没错的=3=)
时间复杂度:O(m*n),只需要动态更新一个二维数组的值即可。
代码
暴力动规:
public boolean isInterleave(String s1, String s2, String s3) {
if (s1 == null || s2 == null || s2 == null)
return false;
if ("".equals(s3) && "".equals(s2) && "".equals(s1))
return true;
if ("".equals(s3) && ((!"".equals(s1)) || (!"".equals(s2))))
return false;
return getResult(s1, s2, s3);
}
private boolean getResult(String s1, String s2, String s3) {
// TODO Auto-generated method stub
if (s3.length() != s1.length() + s2.length())
return false;
if ("".equals(s3) && "".equals(s2) && "".equals(s1))
return true;
if ("".equals(s3) && ((!"".equals(s1)) || (!"".equals(s2))))
return false;
char ch = s3.charAt(0);
boolean ans = false;
if (s1.length() >= 1 && s1.charAt(0) == ch) {
ans = getResult(s1.substring(1), s2, s3.substring(1));
}
if (ans)
return ans;
if (s2.length() >= 1 && s2.charAt(0) == ch) {
ans = getResult(s1, s2.substring(1), s3.substring(1));
}
return ans;
}
带备忘录的动规:
public boolean isInterleave2(String s1, String s2, String s3) {
int[][] memo = new int[s1.length()][s2.length()];
for (int i = 0; i < s1.length(); i++) {
for (int j = 0; j < s2.length(); j++) {
memo[i][j] = -1;
}
}
return getResult2(s1, 0, s2, 0, s3, 0, memo);
}
private boolean getResult2(String s1, int i, String s2, int j, String s3, int k, int[][] memo) {
// TODO Auto-generated method stub
if (i == s1.length()) {
return s3.substring(k).equals(s2.substring(j));
}
if (j == s2.length()) {
return s3.substring(k).equals(s1.substring(i));
}
if (s1.charAt(i) == s3.charAt(k)) {
memo[i][j] = getResult2(s1, i + 1, s2, j, s3, k + 1, memo) ? 1 : 0;
}
if (memo[i][j] == 1)
return true;
if (s2.charAt(j) == s3.charAt(k)) {
memo[i][j] = getResult2(s1, i, s2, j + 1, s3, k + 1, memo) ? 1 : 0;
}
return memo[i][j] == 1 ? true : false;
}
二维数组的动态规划:
public boolean isInterleave3(String s1, String s2, String s3) {
// 方法三 来自Leetcode官方解答(一个有点智障的下午)
// 我几时不智障
if (s1.length() + s2.length() != s3.length())
return false;
boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1];
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
if (i == 0 && j == 0) {
dp[i][j] = true;
} else if (i == 0) {
dp[i][j] = s2.substring(0, j).equals(s3.substring(0, j));
} else if (j == 0) {
dp[i][j] = s1.substring(0, i).equals(s3.substring(0, i));
}
// 比较当前字符与s3的当前位置的字符是否匹配
else {
dp[i][j] = (s3.charAt(i + j - 1) == s1.charAt(i - 1) && dp[i - 1][j])
|| (s3.charAt(i + j - 1) == s2.charAt(j - 1) && dp[i][j - 1]);
}
}
}
return dp[s1.length()][s2.length()];
}
该博客介绍了LeetCode第97题的交错字符串问题,通过动态规划的方法来解决。作者详细解释了暴力动态规划、带备忘录的动态规划以及二维数组动态规划三种解法,并分析了它们的时间复杂度。最后,给出了每种解法的代码实现。
1062

被折叠的 条评论
为什么被折叠?



