2020.7.18 力扣每日
解法一:暴力法
1 class Solution { 2 public boolean isInterleave(String s1, String s2, String s3) { 3 int index1 = 0, index2 = 0, index3 = 0; 4 if (check(s1, index1, s2, index2, s3, index3) == true) 5 return true; 6 return false; 7 } 8 public boolean check(String s1, int index1, String s2, int index2, String s3,int index3) { 9 if(index1 == s1.length()) { 10 if (s3.substring(index3).equals(s2.substring(index2))) 11 return true; 12 else return false; 13 } 14 if(index2 == s2.length()) { 15 if (s3.substring(index3).equals(s1.substring(index1))) 16 return true; 17 else return false; 18 } 19 if(s1.charAt(index1) == s3.charAt(index3)) 20 if (check(s1, index1 + 1, s2, index2, s3, index3 + 1) == true) 21 return true; 22 if(s2.charAt(index2) == s3.charAt(index3)) 23 if (check(s1, index1, s2, index2 + 1, s3, index3 + 1) == true) 24 return true; 25 return false; 26 } 27 }
解题思路:
本题中由于字符串是交错组成,ab中又会出现相同的字母,使用双指针的方法,对于样例s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac",就已经出错了。显然这里需要使用别的方法,如回溯,动态规划,递归等。这里就先使用暴力法,即递归所有的可能性,其递归部分与双指针类似,即使用index1,index2,index3记录下当前的指针,分别比较s1与s3,s2与s3在对应位置的字符,相等则使index++,最终遍历完一个字符串时,只需使用substring比较另一个字符串与s3剩下部分即可。
注意点:
在递归函数中,注意return的时机,如index等于字符串长度时,无论剩余部分是否匹配,都需要return一个值,否则进入后续s1,s2,,s3的字符比较index将越界。
递归调用时同样,只有当返回值为true时才return true,否则继续递归遍历其余可能,遍历完所有可能后再return false。
空间复杂度:O(n*m) n,m为s1,s2的长度
时间复杂度:O(2^k), k为s3的长度
题后总结:
优:利用递归,结构易懂清晰,可读性好
差:频繁的递归会有许多重复的操作,时间复杂度太高
解法二:记忆化优化
1 class Solution { 2 Boolean[][] mem; 3 public boolean isInterleave(String s1, String s2, String s3) { 4 mem = new Boolean[s1.length() + 1][s2.length() + 1]; 5 int index1 = 0, index2 = 0, index3 = 0; 6 if (check(s1, index1, s2, index2, s3, index3) == true) 7 return true; 8 return false; 9 } 10 public boolean check(String s1, int index1, String s2, int index2, String s3,int index3) { 11 if (mem[index1][index2] != null) 12 return mem[index1][index2]; 13 if (index1 == s1.length()) { 14 if(s3.substring(index3).equals(s2.substring(index2))) 15 return true; 16 else return mem[index1][index2] = false; 17 } 18 if (index2 == s2.length()) { 19 if (s3.substring(index3).equals(s1.substring(index1))) 20 return true; 21 else return mem[index1][index2] = false; 22 } 23 if (s1.charAt(index1) == s3.charAt(index3)) 24 if (check(s1, index1 + 1, s2, index2, s3, index3 + 1) == true) 25 return true; 26 if (s2.charAt(index2) == s3.charAt(index3)) 27 if (check(s1, index1, s2, index2 + 1, s3, index3 + 1) == true) 28 return true; 29 return mem[index1][index2] =false; 30 } 31 }
解题思路:
依旧使用递归遍历所有可能性,但对于递归,一般都可使用记忆化的方式来优化时间复杂度,显然此处也可以,设置一个mem数组记录递归的结果,每次进入递归只需判断对应mem的状态,可减少重复操作,该题中显然mem数组的大小可以设置为s1与s2的长度。
注意点:
由于mem记录的是所有的可能性,范围为[0,length],所以定义时数组长度应定义为length()+1。
由于每次需判断对应mem的状态,若定义为boolean类型数组,则初值默认为false,无法满足需求,此处需定义为Boolean类型,这样初值便为null,可以通过if条件判断来减少重复操作。
空间复杂度:O(n*m)
时间复杂度:O(n*m) n,m为s1,s2的长度
题后总结:
优:在递归的基础上使用了记忆化优化,减小了时间复杂度
差:代码仍有些臃肿,还可更简化些。
碎碎念:不过这是第一次这么顺利解决困难难度的题目!而且优化了时间复杂度!全靠自己,快而准确!开心