LeetCode(97):交错字符串

Hard!

题目描述:

给定三个字符串 s1s2s3, 验证 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++解法二:

 1 class Solution {
 2 public:
 3     bool isInterleave(string s1, string s2, string s3) {
 4         if (s1.size() + s2.size() != s3.size()) return false;
 5         int n1 = s1.size(), n2 = s2.size();
 6         vector<vector<bool> > dp(n1 + 1, vector<bool> (n2 + 1, false));
 7         for (int i = 0; i <= n1; ++i) {
 8             for (int j = 0; j <= n2; ++j) {
 9                 if (i == 0 && j == 0) {
10                     dp[i][j] = true;
11                 } else if (i == 0) {
12                     dp[i][j] = dp[i][j - 1] && s2[j - 1] == s3[i + j - 1];
13                 } else if (j == 0) {
14                     dp[i][j] = dp[i - 1][j] && s1[i - 1] == s3[i + j - 1];
15                 } else {
16                     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]);
17                 }
18             }
19         }
20         return dp[n1][n2];
21     }
22 };

这道题也可以使用带优化的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++解法三:

 1 class Solution {
 2 public:
 3     bool isInterleave(string s1, string s2, string s3) {
 4         if (s1.size() + s2.size() != s3.size()) return false;
 5         unordered_set<int> s;
 6         return helper(s1, 0, s2, 0, s3, 0, s);
 7     }
 8     bool helper(string& s1, int i, string& s2, int j, string& s3, int k, unordered_set<int>& s) {
 9         int key = i * s3.size() + j;
10         if (s.count(key)) return false;
11         if (i == s1.size()) return s2.substr(j) == s3.substr(k);
12         if (j == s2.size()) return s1.substr(i) == s3.substr(k);
13         if ((s1[i] == s3[k] && helper(s1, i + 1, s2, j, s3, k + 1, s)) ||
14             (s2[j] == s3[k] && helper(s1, i, s2, j + 1, s3, k + 1, s))) return true;
15         s.insert(key);
16         return false;
17     }
18 };

既然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++解法四:

 1 class Solution {
 2 public:
 3     bool isInterleave(string s1, string s2, string s3) {
 4         if (s1.size() + s2.size() != s3.size()) return false;
 5         int n1 = s1.size(), n2 = s2.size(), n3 = s3.size(), k = 0;
 6         unordered_set<int> s;
 7         queue<int> q{{0}};
 8         while (!q.empty() && k < n3) {
 9             int len = q.size();
10             for (int t = 0; t < len; ++t) {
11                 int i = q.front() / n3, j = q.front() % n3; q.pop();
12                 if (i < n1 && s1[i] == s3[k]) {
13                     int key = (i + 1) * n3 + j;
14                     if (!s.count(key)) {
15                         s.insert(key);
16                         q.push(key);
17                     }
18                 }
19                 if (j < n2 && s2[j] == s3[k]) {
20                     int key = i * n3 + j + 1;
21                     if (!s.count(key)) {
22                         s.insert(key);
23                         q.push(key);
24                     }
25                 }
26             }
27             ++k;
28         }
29         return !q.empty() && k == n3;
30     }
31 };

原文地址:https://www.cnblogs.com/ariel-dreamland/p/9159742.html

时间: 2024-10-10 13:31:13

LeetCode(97):交错字符串的相关文章

[leetcode] 97. 交错字符串

97. 交错字符串 题不难,dfs加回溯即可.实际上就是暴力搜索,穷举所有选择路径. 以s3的每一个字母做一个状态,就面临两个抉择,选s1还是选s2.选s1路不通的话,回溯回来改选s2即可. class Solution { // 状态:String s1, String s2, String s3, int p, int q, int i // p为当前s1的下标指针,q为当前s2的下标指针,i为s3的 boolean dfs(String s1, String s2, String s3,

97. 交错字符串

题目描述: 给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的. 示例 1: 输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"输出: true示例 2: 输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"输出: false 思路: 设dp[i][j]表示

LeetCode:反转字符串中的元音字母【345】

LeetCode:反转字符串中的元音字母[345] 题目描述 编写一个函数,以字符串作为输入,反转该字符串中的元音字母. 示例 1: 输入: "hello" 输出: "holle" 示例 2: 输入: "leetcode" 输出: "leotcede" 说明:元音字母不包含字母"y". 题目分析 所谓的做题就是把以前背下来的拿过来改一下即可.双指针碰撞模型,之前已经描述过很多次了,此处不在赘述. 知道AEI

leetcode python翻转字符串里的单词

# Leetcode 151 翻转字符串里的单词### 题目描述给定一个字符串,逐个翻转字符串中的每个单词. **示例1:** 输入: "the sky is blue" 输出: "blue is sky the" **示例2:** 输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括. **示例3:** 输入: "

leetCode 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. 思路:此题刚开始有点想当然了

[LeetCode] Add Strings 字符串相加

Given two non-negative numbers num1 and num2 represented as string, return the sum of num1 and num2. Note: The length of both num1 and num2 is < 5100. Both num1 and num2 contains only digits 0-9. Both num1 and num2 does not contain any leading zero.

交错字符串——动态规划

题目描述: 给定三个字符串A, B, C,判断C是否由A和B交错构成.交错构成的意思是,对于字符串C,可以将其每个字符标记为A类或B类,使得我A类的每个字符顺序构成了A字符串,B类的每个字符顺序构成了B字符串.如:对于A="rabbit" B="mq", "rabmbitq"是由A和B交错构成的,但"rabbqbitm"不是由A和B交错构成. 解题思路: 1.直接顺序比较,从C中依次拿掉A,看剩下的是不是等于B,若是则返回tr

leetCode 题解之字符串中第一个不重复出现的字符

1.题目描述 Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1. Examples:  s = "leetcode" return 0.  s = "loveleetcode", return 2. Note: You may assume the string contain only

Leetcode 345. 反转字符串中的元音字母 By Python

编写一个函数,以字符串作为输入,反转该字符串中的元音字母. 示例 1: 输入: "hello" 输出: "holle" 示例 2: 输入: "leetcode" 输出: "leotcede" 说明: 元音字母不包含字母"y". 思路 设立2个指针,一个从索引0开始向右,一个从末尾向前,根据条件进行处理即可 代码 class Solution: def reverseVowels(self, s): &quo