Leetcode 97:交错字符串(超详细的解法!!!)

给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1s2 交错组成的。

示例 1:

输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出: true

示例 2:

输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: false

解题思路

这个问题非常简单,我们首先想到的解法就是通过递归。我们定义 f ( s 1 , s 2 , s 3 ) f(s1,s2,s3) f(s1,s2,s3)返回我们最后的结果,那么我们可以分成如下四种情况考虑。

  • s 1 [ 0 ] = s 3 [ 0 ]   &   s 2 [ 0 ] ≠ s 3 [ 0 ] s_1[0]=s_3[0]\ \& \ s_2[0]\neq s_3[0] s1[0]=s3[0] & s2[0]̸=s3[0]
  • s 1 [ 0 ] ≠ s 3 [ 0 ]   &   s 2 [ 0 ] = s 3 [ 0 ] s_1[0]\neq s_3[0]\ \& \ s_2[0] = s_3[0] s1[0]̸=s3[0] & s2[0]=s3[0]
  • s 1 [ 0 ] = s 3 [ 0 ]   &   s 2 [ 0 ] = s 3 [ 0 ] s_1[0]=s_3[0]\ \& \ s_2[0] = s_3[0] s1[0]=s3[0] & s2[0]=s3[0]
  • s 1 [ 0 ] ≠ s 3 [ 0 ]   &   s 2 [ 0 ] ≠ s 3 [ 0 ] s_1[0]\neq s_3[0]\ \& \ s_2[0]\neq s_3[0] s1[0]̸=s3[0] & s2[0]̸=s3[0]

对于第一种情况,我们返回 f ( s 1 [ 1 : ] , s 2 , s 3 [ 1 : ] ) f(s_1[1:],s_2,s_3[1:]) f(s1[1:],s2,s3[1:])。对于第二种情况,我们返回 f ( s 1 , s 2 [ 1 : ] , s 3 [ 1 : ] ) f(s_1,s_2[1:],s_3[1:]) f(s1,s2[1:],s3[1:])。对于第三种情况,我们返回 f ( s 1 [ 1 : ] , s 2 , s 3 [ 1 : ] )   ∣ ∣   f ( s 1 , s 2 [ 1 : ] , s 3 [ 1 : ] ) f(s_1[1:],s_2,s_3[1:]) \ ||\ f(s_1,s_2[1:],s_3[1:]) f(s1[1:],s2,s3[1:])  f(s1,s2[1:],s3[1:])。对于第四种情况,我们返回False。现在我们考虑边界问题。会分成两种情况

  • s 1 s_1 s1为空, s 2 s_2 s2不为空
  • s 2 s_2 s2为空, s 1 s_1 s1不为空
  • s 1 s_1 s1为空, s 2 s_2 s2为空

对于第一种情况,我们首先要判断 s 2 [ 0 ] = = s 3 [ 0 ] s_2[0]==s_3[0] s2[0]==s3[0],如果成立,我们返回 f ( s 1 , s 2 [ 1 : ] , s 3 [ 1 : ] ) f(s_1,s_2[1:],s_3[1:]) f(s1,s2[1:],s3[1:]),否则我们返回False。对于第二种情况,我们首先要判断 s 1 [ 0 ] = = s 3 [ 0 ] s_1[0]==s_3[0] s1[0]==s3[0],如果成立,我们返回 f ( s 1 [ 1 : ] , s 2 , s 3 [ 1 : ] ) f(s_1[1:],s_2,s_3[1:]) f(s1[1:],s2,s3[1:]),否则我们返回False。对于第三种情况,我们返回True

class Solution:
    def isInterleave(self, s1, s2, s3):
        """
        :type s1: str
        :type s2: str
        :type s3: str
        :rtype: bool
        """
        if len(s1) + len(s2) != len(s3):
            return False
        
        if not s1 and not s2:
            return True
        elif not s1:
            return self.isInterleave(s1, s2[1:], s3[1:]) and s2[0] == s3[0]
        elif not s2:
            return self.isInterleave(s1[1:], s2, s3[1:]) and s1[0] == s3[0]

        if s1[0] == s3[0] and s2[0] != s3[0]:
            return self.isInterleave(s1[1:], s2, s3[1:])
        elif s2[0] == s3[0] and s1[0] != s3[0]:
            return self.isInterleave(s1, s2[1:], s3[1:])
        elif s1[0] == s3[0] and s2[0] == s3[0]:
            return self.isInterleave(s1[1:], s2, s3[1:]) or self.isInterleave(s1, s2[1:], s3[1:])
        else:
            return False

我们同样可以使用记忆化搜索优化这个问题,这里我就不再赘述了。我们还可以使用另一种思路,我们将之前定义的函数 f ( s 1 , s 2 , s 3 ) f(s_1,s_2,s_3) f(s1,s2,s3)就需要做一些调整,我们现在定义一个新的 f ( i , j ) f(i,j) f(i,j),其中 i i i j j j表示输入 s 1 s_1 s1 s 2 s_2 s2的位置。我们依旧会考虑四种情况

  • i = 0   &   j ≠ 0 i=0\ \& \ j\neq 0 i=0 & j̸=0
  • i ≠ 0   &   j = 0 i \neq 0\ \& \ j = 0 i̸=0 & j=0
  • i = 0   &   j = 0 i=0\ \& \ j = 0 i=0 & j=0
  • i ≠ 0   &   j ≠ 0 i\neq 0\ \& \ j\neq 0 i̸=0 & j̸=0

对于第一种情况,我们返回 f ( i , j − 1 )   &   s 2 [ j − 1 ] = = s 3 [ i + j − 1 ] f(i,j-1)\ \& \ s_2[j-1]==s_3[i+j-1] f(i,j1) & s2[j1]==s3[i+j1]。对于第二种情况,我们返回 f ( i − 1 , j )   &   s 1 [ i − 1 ] = = s 3 [ i + j − 1 ] f(i-1,j)\ \& \ s_1[i-1]==s_3[i+j-1] f(i1,j) & s1[i1]==s3[i+j1]。对于第三种情况,我们返回True。对于第四种情况,我们返回 ( f ( i − 1 , j )   &   s 1 [ j − 1 ] = = s 3 [ i + j − 1 ] )   ∣ ∣   f ( i , j − 1 )   &   s 2 [ j − 1 ] = = s 3 [ i + j − 1 ] (f(i-1,j)\ \& \ s_1[j-1]==s_3[i+j-1])\ || \ f(i,j-1)\ \& \ s_2[j-1]==s_3[i+j-1] (f(i1,j) & s1[j1]==s3[i+j1])  f(i,j1) & s2[j1]==s3[i+j1]

class Solution:
    def isInterleave(self, s1, s2, s3):
        """
        :type s1: str
        :type s2: str
        :type s3: str
        :rtype: bool
        """
        s1_len, s2_len, s3_len = len(s1), len(s2), len(s3)
        if s1_len + s2_len != s3_len:
            return False
        
        return self._isInterleave(s1_len, s2_len, s1, s2, s3)
        
    def _isInterleave(self, i, j, s1, s2, s3):
        if i == 0 and j == 0:
            return True
        elif i == 0:
            return self._isInterleave(i, j - 1, s1, s2, s3) and (s2[j - 1] == s3[i + j - 1])
        elif j == 0:
            return self._isInterleave(i - 1, j, s1, s2, s3) and (s1[i - 1] == s3[i + j - 1])
        else:
            return (self._isInterleave(i, j - 1, s1, s2, s3) and (s2[j - 1] == s3[i + j - 1])) or \
                   (self._isInterleave(i - 1, j, s1, s2, s3) and (s1[i - 1] == s3[i + j - 1]))       

这种做法比前面的算法好的地方在于我们可以快速的通过动态规划去优化它。

class Solution:
    def isInterleave(self, s1, s2, s3):
        """
        :type s1: str
        :type s2: str
        :type s3: str
        :rtype: bool
        """
        s1_len, s2_len, s3_len = len(s1), len(s2), len(s3)
        if s1_len + s2_len != s3_len:
            return False
        
        mem = [[False]*(s2_len + 1) for _ in range(s1_len + 1)]
        
        for i in range(s1_len + 1):
            for j in range(s2_len + 1):
                if i == 0 and j == 0:
                    mem[i][j] = True
                elif i == 0:
                    mem[i][j] = mem[i][j - 1] and (s2[j - 1] == s3[i + j - 1])
                elif j == 0:
                    mem[i][j] = mem[i - 1][j] and (s1[i - 1] == s3[i + j - 1])
                else:
                    mem[i][j] = (mem[i][j-1] and (s2[j - 1] == s3[i + j - 1])) or \
                                (mem[i - 1][j] and (s1[i - 1] == s3[i + j - 1]))
                    
        return mem[-1][-1]

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值