Leetcode 115:不同的子序列(最详细的解法!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:[email protected] https://blog.csdn.net/qq_17550379/article/details/84098044

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。

一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE""ABCDE" 的一个子序列,而 "AEC" 不是)

示例 1:

输入: S = "rabbbit", T = "rabbit"
输出: 3
解释:

如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。
(上箭头符号 ^ 表示选取的字母)

rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^

示例 2:

输入: S = "babgbag", T = "bag"
输出: 5
解释:

如下图所示, 有 5 种可以从 S 中得到 "bag" 的方案。 
(上箭头符号 ^ 表示选取的字母)

babgbag
^^ ^
babgbag
^^    ^
babgbag
^    ^^
babgbag
  ^  ^^
babgbag
    ^^^

解题思路

我们先进行一些准备工作。

s_len, t_len, result = len(s), len(t), 0
if s_len < t_len:
    return result

if s_len == t_len:
    if s == t:
        return 1
    return result

首先你一定可以想到这样的暴力解法。从S中选出len(T)大小的全部组合,然后判断这些组合中有多少个和T是一样的。

for i in combinations(s, t_len):
    new_t = "".join(i)
    if new_t == t:
        result += 1

这种匹配问题,我们很容易想到通过动态规划来做。但是如果我们直接思考递归过程,很难想出具体的递推公式。对于两个字符串的比较,在我们没有其他办法的时候,我们不妨先建立一个二维的数组,观察一下两个字符串的匹配情况

  Ø r a b b b i t
Ø 1 1 1 1 1 1 1 1
r 0 1 1 1 1 1 1 1
a 0 0 1 1 1 1 1 1
b 0 0 0 1 2 3 3 3
b 0 0 0 0 1 3 3 3
i 0 0 0 0 0 0 3 3
t 0 0 0 0 0 0 0 3 

首先说明一下上面这个矩阵的含义,横向表示S,纵向表示T,每个元素表示当前横坐标向左的S字符串最多可以匹配几次当前纵坐标向上的T字符串。举个例子,对于坐标3,4,此时S=rabb,而T=rab,所以可以匹配两次。观察上面这个矩阵,我们可以的到这样的规律,假设我们此时的坐标是(i,j),如果S[j]==T[i],我们的矩阵mem[i+1][j+1]=mem[i][j]+mem[i+1][j],否则的话mem[i+1][j+1]=mem[i+1][j]

class Solution:
    def numDistinct(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: int
        """
        s_len, t_len = len(s), len(t)
        mem = [[0]*(s_len+1) for _ in range(t_len+1)]
        for i in range(s_len+1):
            mem[0][i] = 1

        for i in range(t_len):
            for j in range(s_len):
                if s[j] == t[i]:
                    mem[i+1][j+1] = mem[i][j] + mem[i+1][j]
                else:
                    mem[i+1][j+1] = mem[i+1][j]

        return mem[-1][-1]

如果我们从另外一个角度去看这个问题,我们按照递归的思路去考虑,每次遍历s的一个元素,然后思考t的前缀在s[:i]中出现的次数,我们会得到这样的矩阵

r a b b i t 
0 0 0 0 0 0 Ø
1 0 0 0 0 0 r
1 1 0 0 0 0 a
1 1 1 0 0 0 b
1 1 2 1 0 0 b
1 1 3 3 0 0 b
1 1 3 3 3 0 i
1 1 3 3 3 3 t

我们不难发现这样的规律,我们每次计算新的一行当中的元素,假设此时的坐标是i,j,如果s[i]==t[j],我们此时的第j+1列的值,会等于原来j+1列的值再加上原来j列的值,也就是↘和↓相加。

class Solution:
    def numDistinct(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: int
        """
        t_len = len(t)
        mem = [1]+[0]*t_len
        for s_c in s:
            for i in range(t_len-1, -1, -1):
                if t[i] == s_c:
                    mem[i+1] += mem[i]

        return mem[-1]

是不是很酷!!!

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

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

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/84098044