LeetCode(97):交错字符串
Hard!
题目描述:
给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
示例 1:
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出: true
示例 2:
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: false
解题思路:
这道求交织相错的字符串和之前那道Word Break 拆分词句 的题很类似,就像之前说的只要是遇到字符串的子序列或是匹配问题直接就上动态规划Dynamic Programming,其他的都不要考虑,什么递归呀的都是浮云,千辛万苦的写了递归结果拿到OJ上妥妥Time Limit Exceeded,所以还是直接就考虑DP解法省事些。一般来说字符串匹配问题都是更新一个二维dp数组,核心就在于找出递推公式。那么我们还是从题目中给的例子出发吧,手动写出二维数组dp如下:

Ø d b b c a
Ø T F F F F F
a T F F F F F
a T T T T T F
b F T T F T F
c F F T T T T
c F F F T F T

首先,这道题的大前提是字符串s1和s2的长度和必须等于s3的长度,如果不等于,肯定返回false。那么当s1和s2是空串的时候,s3必然是空串,则返回true。所以直接给dp[0][0]赋值true,然后若s1和s2其中的一个为空串的话,那么另一个肯定和s3的长度相等,则按位比较,若相同且上一个位置为True,赋True,其余情况都赋False,这样的二维数组dp的边缘就初始化好了。下面只需要找出递推公式来更新整个数组即可,我们发现,在任意非边缘位置dp[i][j]时,它的左边或上边有可能为True或是False,两边都可以更新过来,只要有一条路通着,那么这个点就可以为True。那么我们得分别来看,如果左边的为True,那么我们去除当前对应的s2中的字符串s2[j - 1] 和 s3中对应的位置的字符相比(计算对应位置时还要考虑已匹配的s1中的字符),为s3[j - 1 + i], 如果相等,则赋True,反之赋False。 而上边为True的情况也类似,所以可以求出递推公式为:
dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i - 1 + j]) || (dp[i][j - 1] && s2[j - 1] == s3[j - 1 + i]);
其中dp[i][j] 表示的是 s2 的前 i 个字符和 s1 的前 j 个字符是否匹配 s3 的前 i+j 个字符,根据以上分析,可写出代码如下:
C++解法一:

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

我们也可以把for循环合并到一起,用if条件来处理边界情况,整体思路和上面的解法没有太大的区别。
C++解法二:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s1.size() + s2.size() != s3.size()) return false;
int n1 = s1.size(), n2 = s2.size();
vector<vector<bool> > dp(n1 + , vector<bool> (n2 + , false));
for (int i = ; i <= n1; ++i) {
for (int j = ; j <= n2; ++j) {
if (i == && j == ) {
dp[i][j] = true;
} else if (i == ) {
dp[i][j] = dp[i][j - ] && s2[j - ] == s3[i + j - ];
} else if (j == ) {
dp[i][j] = dp[i - ][j] && s1[i - ] == s3[i + j - ];
} else {
dp[i][j] = (dp[i - ][j] && s1[i - ] == s3[i + j - ]) || (dp[i][j - ] && s2[j - ] == s3[i + j - ]);
}
}
}
return dp[n1][n2];
}
};
这道题也可以使用带优化的DFS来做,我们使用一个哈希集合,用来保存匹配失败的情况,我们分别用变量i,j,和k来记录字符串s1,s2,和s3匹配到的位置,初始化的时候都传入0。在递归函数中,首先根据i和j,算出key值,由于我们的哈希集合中只能放一个数字,而我们要encode两个数字i和j,所以通过用i乘以s3的长度再加上j来得到key,此时我们看,如果key已经在集合中,直接返回false,因为集合中存的是无法匹配的情况。然后先来处理corner case的情况,如果i等于s1的长度了,说明s1的字符都匹配完了,此时s2剩下的字符和s3剩下的字符可以直接进行匹配了,所以我们直接返回两者是否能匹配的bool值。同理,如果j等于s2的长度了,说明s2的字符都匹配完了,此时s1剩下的字符和s3剩下的字符可以直接进行匹配了,所以我们直接返回两者是否能匹配的bool值。如果s1和s2都有剩余字符,那么当s1的当前字符等于s3的当前字符,那么调用递归函数,注意i和k都加上1,如果递归函数返回true,则当前函数也返回true;还有一种情况是,当s2的当前字符等于s3的当前字符,那么调用递归函数,注意j和k都加上1,如果递归函数返回true,那么当前函数也返回true。如果匹配失败了,则将key加入集合中,并返回false即可。
C++解法三:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s1.size() + s2.size() != s3.size()) return false;
unordered_set<int> s;
return helper(s1, , s2, , s3, , s);
}
bool helper(string& s1, int i, string& s2, int j, string& s3, int k, unordered_set<int>& s) {
int key = i * s3.size() + j;
if (s.count(key)) return false;
if (i == s1.size()) return s2.substr(j) == s3.substr(k);
if (j == s2.size()) return s1.substr(i) == s3.substr(k);
if ((s1[i] == s3[k] && helper(s1, i + , s2, j, s3, k + , s)) ||
(s2[j] == s3[k] && helper(s1, i, s2, j + , s3, k + , s))) return true;
s.insert(key);
return false;
}
};
既然DFS可以,那么BFS也就坐不住了,也要出来浪一波。这里我们需要用队列queue来辅助运算,如果将解法一讲解中的那个二维dp数组列出来的TF图当作一个迷宫的话,那么BFS的目的就是要从(0, 0)位置找一条都是T的路径通到(n1, n2)位置,这里我们还要使用哈希集合,不过此时保存到是已经遍历过的位置,队列中还是存key值,key值的encode方法跟上面DFS解法的相同,初识时放个0进去。然后我们进行while循环,循环条件除了q不为空,还有一个是k小于n3,因为匹配完s3中所有的字符就结束了。然后由于是一层层的遍历,所以要直接循环queue中元素个数的次数,在for循环中,对队首元素进行解码,得到i和j值,如果i小于n1,说明s1还有剩余字符,如果s1当前字符等于s3当前字符,那么把s1的下一个位置i+1跟j一起加码算出key值,如果该key值不在于集合中,则加入集合,同时加入队列queue中;同理,如果j小于n2,说明s2还有剩余字符,如果s2当前字符等于s3当前字符,那么把s2的下一个位置j+1跟i一起加码算出key值,如果该key值不在于集合中,则加入集合,同时加入队列queue中。for循环结束后,k自增1。最后如果匹配成功的话,那么queue中应该只有一个(n1, n2)的key值,且k此时等于n3,所以当queue为空或者k不等于n3的时候都要返回false。
C++解法四:
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
if (s1.size() + s2.size() != s3.size()) return false;
int n1 = s1.size(), n2 = s2.size(), n3 = s3.size(), k = ;
unordered_set<int> s;
queue<int> q{{}};
while (!q.empty() && k < n3) {
int len = q.size();
for (int t = ; t < len; ++t) {
int i = q.front() / n3, j = q.front() % n3; q.pop();
if (i < n1 && s1[i] == s3[k]) {
int key = (i + ) * n3 + j;
if (!s.count(key)) {
s.insert(key);
q.push(key);
}
}
if (j < n2 && s2[j] == s3[k]) {
int key = i * n3 + j + ;
if (!s.count(key)) {
s.insert(key);
q.push(key);
}
}
}
++k;
}
return !q.empty() && k == n3;
}
};
LeetCode(97):交错字符串的更多相关文章
- Java实现 LeetCode 97 交错字符串
97. 交错字符串 给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的. 示例 1: 输入: s1 = "aabcc", s2 = " ...
- [每日一题2020.06.09] leetcode #97 交错字符串 dp
题目链接 利用动态规划的思想, 对于每种状态(i, j)来说都有(i-1, j) 和 (i,j-1) 需要注意的问题 : 初始化的问题,先把i=0和j=0的状态都初始化后才可以进行dp否则发生数组越界 ...
- C#LeetCode刷题-字符串
字符串篇 # 题名 刷题 通过率 难度 3 无重复字符的最长子串 24.6% 中等 5 最长回文子串 22.4% 中等 6 Z字形变换 35.8% 中等 8 字符串转整数 (atoi) ...
- C#版(击败97.76%的提交) - Leetcode 557. 反转字符串中的单词 III - 题解
版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. Leetcod ...
- C#版(击败100.00%的提交) - Leetcode 151. 翻转字符串里的单词 - 题解
版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C#版 - L ...
- LeetCode:反转字符串中的元音字母【345】
LeetCode:反转字符串中的元音字母[345] 题目描述 编写一个函数,以字符串作为输入,反转该字符串中的元音字母. 示例 1: 输入: "hello" 输出: "h ...
- LeetCode初级算法--字符串01:反转字符串
LeetCode初级算法--字符串01:反转字符串 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.ne ...
- 前端与算法 leetcode 344. 反转字符串
目录 # 前端与算法 leetcode 344. 反转字符串 题目描述 概要 提示 解析 解法一:双指针 解法二:递归 算法 传入测试用例的运行结果 执行结果 GitHub仓库 # 前端与算法 lee ...
- LeetCode初级算法--字符串02:字符串中的第一个唯一字符
LeetCode初级算法--字符串02:字符串中的第一个唯一字符 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog. ...
- leetcode python反转字符串中的单词
# Leetcode 557 反转字符串中的单词III### 题目描述 给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序. **示例1:** 输入: "L ...
随机推荐
- layer兼容性问题
一.Layer 弹出层在ie8错乱的解决办法 弹出层在火狐.谷歌.360极速模式.IE6下都能100%面积正常显示,但在IE8和360的兼容模式下只显示弹出层下半部分或右半部分的内容,在主页面加上: ...
- [转] Python Traceback详解
追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a Python Traceback详解 刚接触Python的时候,简单的 ...
- 华为交换机有关BGP的相关配置
作者:邓聪聪 上图是本人在某公司任职期间的一次割接任务,在原有的路由器上新配置的另一台高性能的路由器,两台设备为并行 割接要求: 1:原有的网络结构无变化,并行新设备 2:原有设备下的所有用户无变化 ...
- python3+selenium框架设计05-配置文件和浏览器引擎类
python3配置文件的增删改查等操作可以使用内置的ConfigParser模块,可以自行百度学习,也可以看Python3学习笔记27-ConfigParser模块 配置文件一般存放着环境信息,比如u ...
- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path 解决办法
♦ 未在 Java构建路径中 找到父类 "javax.servlet.http.HttpServlet" ♦ 解决办法: 项目右击 → Build Path → 右侧 Add L ...
- hibernate框架学习第三天:对象状态、一级缓存、快照等
对象的状态 瞬时状态: 瞬时对象(TO) 应用程序创建出来的对象,不受H3控制 注意:TO对象不具有OID,一旦为TO赋值OID,那么此时就不是TO 持久化状态:持久化对象(PO) 受H3控制的对象, ...
- linux 新机器的配置(git + nodejs+ mongodb)
安装nodejs: wget https://nodejs.org/dist/v6.9.5/node-v6.9.5-linux-x64.tar.xz tar xvf node-v6.9.5-linux ...
- zabbix-3.0.4添加对windows 2008r2的监控
zabbix-3.0.4添加对windows 2008r2的监控 一.windows客户端的配置关闭windows防火墙或者开通10050和10051端口(1).关闭防火墙(不推荐直接关闭,测试可以这 ...
- 增加一台web机注意事项
2017年4月18日 15:23:57 星期二 增加一台web机时, 先不要挂载进lb 1. 需要将此机器的ip加入到其它服务的白名单内: 数据库, 缓存, 第三方接口等 2. 绑定hosts, 点点 ...
- Docker入门 - 006 Docker 多种数据库的安装
Docker 安装 MySQL 查找Docker Hub上的mysql镜像 root@VM_16_14_centos ~# docker search mysql INDEX NAME DESCRIP ...