LeetCode 笔记系列 20 Interleaving String [动态规划的抽象]
题目: Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
For example,
Given:
s1 = "aabcc",
s2 = "dbbca",
When s3 = "aadbbcbcac", return true.
When s3 = "aadbbbaccc", return false.
这个题目值得记录的原因除了自己的解法没有通过大集合以外,主要还在与对动态规划的理解。在这两个月研究算法的过程中,我发现自己更倾向于直观的理解,而抽象思维上相对较弱。我们以这道题做个例子。
直观上,我看到该题,就会去想,s1取一部分,s2取一部分,然后再s1取一部分,反复知道匹配完成s3,算法去模拟这样的操作。
而当s1和s3匹配了一部分的时候,剩下s1‘和剩下的s3‘与s2又是一个子问题。这样很容易写成一个递归,但是需要注意两点:
1. 递归方法中,我们总是拿s1首先去匹配s3,如果不匹配,直接返回false。这样做的原因是保持匹配是“交替”进行的;
2. 当出现既可以匹配s1,又可以匹配s2的时候,一样可以通过递归来解决,看下面的代码。
private boolean isInterleaveInternal(String s1, String s2, String s3){
if(s1.equals("")) {
return s2.equals("") && s3.equals("");
}
if(s1.equals(s3) && s2.endsWith("")) return true;
int i1 = 0;
int i2 = 0;
int i3 = 0;
if(s1.charAt(0) != s3.charAt(0)) return false;
while(i1 < s1.length() && i2 < s2.length() && i3 < s3.length() &&
s1.charAt(i1) == s3.charAt(i3)) {
i1++;
i3++;
//如果这里s2也可以匹配s3,那么我们立马递归进行匹配
if(s2.charAt(i2) == s3.charAt(i3) && isInterleaveInternal(s2.substring(i2), s1.substring(i1), s3.substring(i3)))
return true;
}
//接下来开始匹配s2
return isInterleaveInternal(s2, s1.substring(i1), s3.substring(i3));
}
所以在调用这个方法的时候,也比较复杂,需要保证一定是s1首先匹配s3.
public boolean isInterleave(String s1, String s2, String s3) {
// Start typing your Java solution below
// DO NOT write main() function
if(s1.length() + s2.length() != s3.length()) return false;
if(s1.equals("") || s2.equals("") || s3.equals("")) {
if(s3.equals("")) return s1.equals("") && s2.equals("");
else return s1.equals(s3) || s2.equals(s3);
}
if(s1.charAt(0) == s3.charAt(0)) {
if(s2.charAt(0) != s3.charAt(0)) {
return isInterleaveInternal(s1, s2, s3);
}else {
if(isInterleaveInternal(s1, s2, s3)) return true;
else return isInterleaveInternal(s2, s1, s3);
}
}else if(s2.charAt(0) == s3.charAt(0)) return isInterleave(s2, s1, s3);
else return false;
}
这个办法看上去蛮直观的,是我马上能想到的,而且也是收到前面递归方法的影响。
但是大集合会超时,而且不好的地方是主函数有挺多的条件判断,显得不够简洁。
于是我们参考了这里的动态规划方法。
动态规划矩阵matched[l1][l2]表示s1取l1长度(最后一个字母的pos是l1-1),s2取l2长度(最后一个字母的pos是l2-1),是否能匹配s3的l1+12长度。
那么,我们有
matched[l1][l2] = s1[l1-1] == s3[l1+l2-1] && matched[l1-1][l2] || s2[l2 - 1] == s3[l1+l2-1] && matched[l1][l2-1]
边界条件是,其中一个长度为0,另一个去匹配s3.
这里s1和s2交替出现的规律并不明显,所以没有直观地想到。
代码如下:
public boolean isInterleave2(String s1, String s2, String s3){
if(s1.length() + s2.length() != s3.length()) return false;
boolean[][] matched = new boolean[s1.length() + 1][s2.length() + 1];
matched[0][0] = true;
for(int i1 = 1; i1 <= s1.length(); i1++){
if(s3.charAt(i1-1) == s1.charAt(i1-1)) {
matched[i1][0] = true;
}else break;
}
for(int i2 = 1; i2 <= s2.length(); i2++){
if(s3.charAt(i2 - 1) == s2.charAt(i2 - 1)) {
matched[0][i2] = true;
}else break;
}
for(int i1 = 1; i1 <= s1.length(); i1++){
char c1 = s1.charAt(i1 - 1);
for(int i2 = 1; i2 <= s2.length(); i2++){
int i3 = i1 + i2;
char c2 = s2.charAt(i2 - 1);
char c3 = s3.charAt(i3 - 1);
if(c1 == c3){
matched[i1][i2] |= matched[i1 - 1][i2];
}
if(c2 == c3){
matched[i1][i2] |= matched[i1][i2 - 1];
}
}
}
return matched[s1.length()][s2.length()];
}
总结下:
1)递归能写出比较清晰简单的代码,但是有比较高的时间复杂度;
2)在递归不满足条件的情况下,动态规划是个比较好的选择;
3)一般来说,独立变量的个数决定动态规划的维度,例如l1和l2独立变化,所以用了二维动态规划。
LeetCode 笔记系列 20 Interleaving String [动态规划的抽象]的更多相关文章
- LeetCode 笔记系列 19 Scramble String [合理使用递归]
题目: Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty subs ...
- LeetCode(97) Interleaving String
题目 Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example, Given: ...
- LeetCode 笔记系列16.3 Minimum Window Substring [从O(N*M), O(NlogM)到O(N),人生就是一场不停的战斗]
题目:Given a string S and a string T, find the minimum window in S which will contain all the characte ...
- LeetCode 笔记系列八 Longest Valid Parentheses [lich你又想多了]
题目:Given a string containing just the characters '(' and ')', find the length of the longest valid ( ...
- LeetCode 笔记系列 18 Maximal Rectangle [学以致用]
题目: Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones ...
- LeetCode 笔记系列16.2 Minimum Window Substring [从O(N*M), O(NlogM)到O(N),人生就是一场不停的战斗]
题目:Given a string S and a string T, find the minimum window in S which will contain all the characte ...
- LeetCode 笔记系列16.1 Minimum Window Substring [从O(N*M), O(NlogM)到O(N),人生就是一场不停的战斗]
题目: Given a string S and a string T, find the minimum window in S which will contain all the charact ...
- LeetCode 笔记系列五 Generate Parentheses
题目: Given n pairs of parentheses, write a function to generate all combinations of well-formed paren ...
- LeetCode 笔记系列13 Jump Game II [去掉不必要的计算]
题目: Given an array of non-negative integers, you are initially positioned at the first index of the ...
随机推荐
- Asp.net 之Application
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- jQuery validation学习(2)验证身份证
验证邮编 jQuery.validator.addMethod("isZipCode", function(value, element) { -]{}$/; return thi ...
- 【Java】聊聊常用的非对称加密算法之一RSA的使用(Java)
参考的优秀文章 Java加密技术(四)——非对称加密算法RSA RSA算法原理(一).RSA算法原理(二) RSA的公钥和私钥到底哪个才是用来加密和哪个用来解密? 简单的介绍 RSA是有名的非对称加密 ...
- winfrom的两种拖拽方式和界面阴影
方式1private Point mouseOffset; //记录鼠标指针的坐标 private bool isMouseDown = false; //记录鼠标按键是否按下 private voi ...
- HTML布局与框架
HTML块 HTML块元素 块元素在显示时,通常会以新行开始 如:<h1>.<p>.<ul> <!DOCTYPE html> <html> ...
- 基于struts2、spring的应用闲置一段时间后报空指针错(转)
在做struts2.spring网站时,在系统闲置一段时间后,访问页面会出错,第二次再访问就正常了.后来查了后台日志,发现是数据库连接关闭了,导致页面访问出错.页面上报空指针错误,错误没有保留,日志中 ...
- 学习mongo系列(十一)关系
准备工作:首先在maxh数据库的address集合中先插入数据 > db.address.insert({child_address:"gansu"}) WriteResul ...
- Excle隐藏及展开列
当excle文档类目比较多的时候我们希望看第一列和某一列的对应关系可以选择隐藏中间列. 选中要隐藏的列,然后右键-->隐藏即可 需要展开的时候,选中:被隐藏列的前一列和后一列,然后当鼠标在列头( ...
- poj1625Censored!(AC自动机+dp)
链接 第一次做这种题目,参考了下题解,相当于把树扯直了做DP,估计这一类题都是这个套路吧. 状态方程dp[i][next] = dp[i][next]+dp[i][j] ;dp[i][j]表示长度为i ...
- 关于JSPatch热修复
今天和同事聊到JSPatch热修复,我们来看下JSPatch,确实解决了APP升级的痛点. 刚好,已经有这么一个第三方平台实现了后台管理,全套服务(网址是:http://jspatch.com/),先 ...