97. 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.

类似于最长公共子序列,从字符尾部开始处理,解题思路很容易找到,递归来做很简单,但是会超时。

class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if(s3.length() != s1.length() + s2.length())
return false;
return isInterleave(s1, s2, s3, s1.length() - 1, s2.length() - 1, s3.length() - 1);
}
private:
bool isInterleave(string &s1, string &s2, string &s3, int i1, int i2, int i3) {
if(i3 < 0) //i3最先到-1
return i1 < 0 && i2 < 0;
return (s1[i1] == s3[i3] && isInterleave(s1, s2, s3, i1 - 1, i2, i3 - 1)) ||
(s2[i2] == s3[i3] && isInterleave(s1, s2, s3, i1, i2 - 1, i3 - 1));
}
};

其实递归中用不上i1、i2、i3这3个状态标志,因为任意两个标志可以表示第三个标志,状态的设计对解题有时很关键。

可以看出该问题满足最优子结构特征和重叠子问题特征,那么试着使用动态规划来改进时间复杂度。

dp[i][j]表示s[0..i]s2[0..j]匹配s3[0..(i + j)],则状态转移方程为:

dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i + j - 1]) || (dp[i][j - 1] && s2[j - 1] == s3[i + j - 1]),子问题数目为O(n2),每个子问题需要用到O(n0)个子问题的结果,跟最长公共子序列问题一样,同属于2D/0D问题。

这是一个二维动态规划问题,边界条件即当i = 0j = 0时,当达到边界条件时就退化为一维动态规划问题。

i = 0时,状态转移方程退化为dp[0][j] = (dp[0][j - 1] && s2[j - 1] == s3[j - 1])

j = 0时,状态转移方程退化为dp[i][0] = (dp[i - 1][0] && s1[i - 1] == s3[i - 1])

可以提前把边界情况计算好,也可以边填表边计算,一般很难说哪种好一些,不过在该情况下实测边填表边计算要好一些。

状态转移图如下,横轴表示s1,纵轴表示s2,其中每一个状态必须访问图中左下角的状态,那么可以先解决左下角的子问题,再计算原问题,这样避免重复计算,最终返回dp[s1.length()][s2.length()]即可。该算法时间复杂度为O(N2),空间复杂度为O(N2)。

提前把边界情况计算好,代码如下。

class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if(s3.length() != s1.length() + s2.length())
return false;
vector<vector<bool>> dp(s1.length() + 1, vector<bool>(s2.length() + 1, true));
for(size_t i = 1; i <= s1.length(); ++i)
dp[i][0] = dp[i - 1][0] && s1[i - 1] == s3[i - 1];
for(size_t j = 1; j <= s2.length(); ++j)
dp[0][j] = dp[0][j - 1] && s2[j - 1] == s3[j - 1];
for(size_t i = 1; i <= s1.length(); ++i) {
for(size_t j = 1; j <= s2.length(); ++j) {
dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i + j - 1]) ||
(dp[i][j - 1] && s2[j - 1] == s3[i + j - 1]);
}
}
return dp[s1.length()][s2.length()];
}
}; //使用滚动数组优化
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if(s1.length() + s2.length() != s3.length())
return false;
if(s1.length() < s2.length())
return isInterleave(s2, s1, s3);
vector<bool> dp(s2.length() + 1, true);
for(size_t i = 1; i <= s2.length(); ++i)
dp[i] = s2[i - 1] == s3[i - 1] && dp[i - 1];
for(size_t i = 1; i <= s1.length(); ++i) {
dp[0] = s1[i - 1] == s3[i - 1] && dp[0];
for(size_t j = 1; j <= s2.length(); ++j)
dp[j] = (dp[j] && s1[i - 1] == s3[i + j - 1]) ||
(dp[j - 1] && s2[j - 1] == s3[i + j - 1]);
}
return dp[s2.length()];
}
};

边填表边计算,代码如下。

class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if(s3.length() != s1.length() + s2.length())
return false;
bool dp[s1.length() + 1][s2.length() + 1];
for(size_t i = 0; i <= s1.length(); i++) {
for(size_t j = 0; j <= s2.length(); j++) {
if(i == 0 && j == 0)
dp[i][j] = true;
else if(i == 0)
dp[i][j] = (dp[i][j - 1] && s2[j - 1] == s3[i + j - 1]);
else if(j == 0)
dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i + j - 1]);
else
dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i + j - 1]) ||
(dp[i][j - 1] && s2[j - 1] == s3[i + j - 1]);
}
}
return dp[s1.length()][s2.length()];
}
};

二维动态规划——Interleaving String的更多相关文章

  1. 动态规划小结 - 二维动态规划 - 时间复杂度 O(n*n)的棋盘型,题 [LeetCode] Minimum Path Sum,Unique Paths II,Edit Distance

    引言 二维动态规划中最常见的是棋盘型二维动态规划. 即 func(i, j) 往往只和 func(i-1, j-1), func(i-1, j) 以及 func(i, j-1) 有关 这种情况下,时间 ...

  2. 543A - Writing Code(二维动态规划)

    题意:现在要写m行代码,总共有n个文件,现在给出第i个文件每行会出现v[i]个bug,问你在bug少于b的条件下有多少种安排 分析:定义dp[i][j][k],i个文件,用了j行代码,有k个bug 状 ...

  3. 二维动态规划&&二分查找的动态规划&&最长递增子序列&&最长连续递增子序列

    题目描述与背景介绍 背景题目: [674. 最长连续递增序列]https://leetcode-cn.com/problems/longest-continuous-increasing-subseq ...

  4. [leetcode] 72. 编辑距离(二维动态规划)

    72. 编辑距离 再次验证leetcode的评判机有问题啊!同样的代码,第一次提交超时,第二次提交就通过了! 此题用动态规划解决. 这题一开始还真难到我了,琢磨半天没有思路.于是乎去了网上喵了下题解看 ...

  5. HDU 1117 免费馅饼 二维动态规划

    思路:a[i][j]表示j秒在i位置的数目,dp[i][j]表示j秒在i位置最大可以收到的数目. 转移方程:d[i][j]=max(dp[i-1][j],dp[i-1][j-1],dp[i-1][j+ ...

  6. 二维动态规划——Palindrome

    Palindrome Description A palindrome is a symmetrical string, that is, a string read identically from ...

  7. LeetCode 笔记系列 20 Interleaving String [动态规划的抽象]

    题目: Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given: ...

  8. LeetCode之“动态规划”:Interleaving String

    题目链接 题目要求: Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example ...

  9. [LeetCode] Interleaving String - 交织的字符串

    题目如下:https://oj.leetcode.com/problems/interleaving-string/ Given s1, s2, s3, find whether s3 is form ...

随机推荐

  1. POJ Sudoku 数独填数 DFS

    题目链接:Sudoku Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 18105   Accepted: 8772   Sp ...

  2. Android---Parcelable包装类的作用

    android提供了一种新的类型:Parcel.本类被用作封装数据的容器,封装后的数据可以通过Intent或IPC传递. 除了基本类型以外,只有实现了Parcelable接口的类才能被放入Parcel ...

  3. AIDL原理解析

    首先为什么需要aidl? 下面是不需要aidl 的binder的IPC通讯过程,表面上结构很简单,但是有个困难就是,客户端和服务端进行通讯,你得先将你的通讯请求转换成序列化的数据,然后调用transa ...

  4. HDU 1540 POJ 2892 Tunnel Warfare

    线段树 区间合并 单点修改 区间查询.又是1秒钟构思,差错查了好久... ... 发现一个int型的定义成了char型,打脸. #include <stdio.h> #include &l ...

  5. Java谜题心得

    1,二进制浮点数的运算是对实际算数的一种近似运算. 2,IEEE 754浮点算术保留了一个特殊的值用来表示一个不是数字的数量[IEEE 754].这个值就是NaN(“不是一个数字(Not a Numb ...

  6. MemSQL 取代 HDFS 与 Spark 结合,性能大幅提升

    MemSQL 取代 HDFS 与 Spark 结合,性能大幅提升 3,597 次阅读 - 基础架构 Apache Spark是目前非常强大的分布式计算框架.其简单易懂的计算框架使得我们很容易理解.虽然 ...

  7. 如何理解java的引用传递

    1. 数组的引用传递 public class TestArray { public static void changeAry1(int[] ary){ int[] ary1 = {9,9,9}; ...

  8. Lua学习系列(一)

    从现在开始,打算学习一门新的脚本语言-lua. 1.什么是lua? a) lua1 • Lua 1.0 was implemented as a library, in less then 6000 ...

  9. linux 驱动入门5

    慢慢的开始转驱动,目前比较有时间,一定要把驱动学会.哎.人生慢慢路,一回头.已经工作了八九年了.努力.在买套房.改退休了.学驱动.个人认为首先要熟悉驱动框架.慢慢来.心急吃不了热豆腐. 看网上都说的设 ...

  10. Android高斯模糊技术,实现毛玻璃效果(转)

    本博客转自郭霖公众号:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650235930&idx=1&sn=e328 ...