不同的子序列

题目描述:给出字符串S和字符串T,计算S的不同的子序列中T出现的个数。子序列字符串是原始字符串通过删除一些(或零个)产生的一个新的字符串,并且对剩下的字符的相对位置没有影响。(比如,“ACE”是“ABCDE”的子序列字符串,而“AEC”不是)。 

样例:给出S = "rabbbit", T = "rabbit",返回 3


可以尝试用动态规划建立二维表格record,其中,record[i][j]表示字符串S的前i个字符构成的序列的子序列中,字符串T的前j项构成的序列的个数。对于这样的动态规划问题,难点就在于建立问题与其子问题之间的关系,也就是状态转移方程。

那现在来观察规律,我们不妨先将题意换个说法,与其说S的不同子序列中T出现的次数,不如说将S通过删除字符的方法转换成T的方法有几种。比如,样例中,S = "rabbbit",中间有3个"b",我们删除其中任何一个,都能得到 T = "rabbit"。这一点题目中其实也说了:“子序列字符串是原始字符串通过删除一些(或零个)产生的一个新的字符串”。


明白了这一点,我们就可以写出状态转移方程了。record[i][j]表示的是,S的前 i 项,通过删除一些字符,得到T的前 j 项,所用的方法种数。那么,当S由前 i - 1项升级到前 i 项的关键就在于多了一个S[i]。


如果S[i] != T[j],那这意味着什么?意味着我要从S的前 i 项通过删除变换到T的前 j 项就必须要删除S[i],因此S的前 i - 1项通过某些方法删除字符,得到T的前 j 项,增加了S[i] 之后,还用这些方法,只不过,每种方法中,还要增加一个删除S[i] 的操作。

举例说,S = "rabbc", T = "rab",当处理S的前4项“rabb”变换到T时(就假设j = 3,我们从此处开始把T的前 j 项就表述成T了),我们知道有两种方法:

1. 删除"rabb"中的第1个b

2. 删除"rabb"中的第2个b

现在,S升级为"rabbc"了,新增加的字符与T的最后一个字符不等,那么就还是刚才那两种方法,但是每一种方法添加一步删除"c"的操作。如下:

1. 删除"rabbc"中的第1个b,再删除最后的"c"

2. 删除"rabbc"中的第2个b,再删除最后的"c"

所以说,当S[i] != T[j]时,方法数与不含S[i]时是一样的:record[i][j] = record[i - 1][j]


而如果S[i] == T[j],情况就会稍微复杂一点。因为此时由S的前 i 项转换到T的前 j 项时,不一定非要删除S[i]了。我们将刚才那个例子变一下:S = "rabbb", T = "rab"

由S的前4项“rabb”变换到T时,有两种方法(和上面一样):

1. 删除"rabb"中的第1个b

2. 删除"rabb"中的第2个b

升级S,S = "rabbb",最后一个字符与T的最后一个字符相等,那就要分两种情况讨论了:

1. 删除S = "rabbb"的最后一个b,此时,与S的前4项变换到T的方法是一致的,就是我上面写的两种:删除"rabbb"的第1个或第2个"b"

2. 不删S = "rabbb"的最后一个b,此时,要使S变换成T,则需要让S的前 i - 1项变换成T的前 j - 1项,也就是"rabb"变换成"ra"。

总结一下,S = "rabbb"通过删除字符变换到T = "rab",一共三种方法:

1. 保留最后一个"b"时,删除第1个和第2个"b"(1种)

2. 不保留最后一个"b"时,删除第1个和第3个"b",或者删除第2个和第3个"b"

所以状态转移方程为:record[i][j] = record[i - 1][j] + record[i - 1][j - 1]

可知,状态转移方程分两种情况,有两个。

record[i][j] = record[i - 1][j] + record[i - 1][j - 1] (S[i] == T[j])

record[i][j] = record[i - 1][j] (S[i] != T[j])


给出代码吧:

class Solution: 
    # @param S, T: Two string.
    # @return: Count the number of distinct subsequences
    def numDistinct(self, S, T):
        m = len(S)
        n = len(T)
        if m == 0:
            return 0

        # 对二维表格拓展,增加一行一列,保存的是S和T为空字符串的情况
        record = [[0 for j in range(n + 1)] for i in range(m + 1)]

        # T = ""时,S不管是多少都只有一种方法转换成T
        # 另外,S = ""时,除非T = "",否则没有方法转换(因为只能是删除嘛)
        # 所以说,表格的第一行都是0(除了record[0][0]这个位置),第一列都是1
        for i in range(m + 1):
            record[i][0] = 1

        i, j = 1, 1
        while i < m + 1:
            j = 1
            while j < n + 1:
                if S[i - 1] == T[j - 1]:
                    record[i][j] = record[i - 1][j] + record[i - 1][j - 1]
                else:
                    record[i][j] = record[i - 1][j]
                j += 1
            i += 1
        return record[m][n]
        # write your code here



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值