代码随想录 day48 动态规划part12 判断子序列 不同的子序列

392.判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false
提示:

0 <= s.length <= 100
0 <= t.length <= 10^4
两个字符串都只由小写字符组成。

思路

s 是否为t的子序列。 t可以删除 or 不删除
dp[i][j] 为 t 的 0 ~ i 包括 i 对 s 的匹配位置为dp[i][j]

if t[j] == s[i]: dp[i][j] = j
else:dp[i][j] = j-1

dp [[s + 1] t + 1]

dp = 0
for i in range(len1):  ##  t
    for j in range(len2):  ## s
        if t[i] == t[j] :
            dp[i+1][j+1] = dp[i][j]
        else:
            dp[i+1][j+1] = max(dp[i-1][j+1], dp[i+1][j])

输入:s = "abc", t = "ahbgdc"
len1 = 6, len2 = 3

dp[1][1] = 0
dp[1][2] = 0
dp[1][3] = 0

dp[2][1] = 0
dp[2][2] = 0
dp[2][3] = 1

dp[3][1] = 0
dp[3][2] = 0
dp[3][3] = 1

dp[6][1] = 0
dp[6][2] = 0
dp[6][3] = 3

dp[-1][-1]

dp[j] = j

dp[j] = dp[j-1]

for j in range(len2):  ## s
    while i < len1:
        if t[i] == t[j]:
            dp[j] = j
            break
        else:
            dp[i] = dp[i-1]
        i += 1

输入:s = "abc", t = "ahbgdc"
len1 = 6, len2 = 3

dp = [0] *( len(t) + 1)
dp[0] = 0
----------
dp[1] = 0
dp[2] = 1
dp[3] = 1
dp[4] = 1
dp[5] = 1
dp[6] = 2

return dp[-1] + 1 == len(s)

code python 1

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        lens, lent = len(s), len(t)
        if lens == 0:return True
        dp = [-1 for _ in range(lent + 1)]
        it = 0
        for j in range(lens):
            while it < lent:
                 if s[j] == t[it]:
                     dp[it + 1] = j
                     it += 1
                     break

                 else:
                     dp[it + 1] = dp[it]
                     it += 1
        for i in range(it + 1, lent):
            dp[i] = dp[i-1]
        return dp[it] + 1 == lens

思路 from代码随想录

这道题也可以用双指针的思路来实现,时间复杂度也是O(n))

这道题应该算是编辑距离的入门题目,因为从题意中我们也可以发现,只需要计算删除的情况,不用考虑增加和替换的情况。

所以掌握本题的动态规划解法是对后面要讲解的编辑距离的题目打下基础。

动态规划五部曲分析如下:

确定dp数组(dp table)以及下标的含义
dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。

注意这里是判断s是否为t的子序列。即t的长度是大于等于s的。

确定递推公式
在确定递推公式的时候,首先要考虑如下两种操作,整理如下:

if (s[i - 1] == t[j - 1])
t中找到了一个字符在s中也出现了
if (s[i - 1] != t[j - 1])
相当于t要删除元素,继续匹配
if (s[i - 1] == t[j - 1]),那么dp[i][j] = dp[i - 1][j - 1] + 1;,因为找到了一个相同的字符,相同子序列长度自然要在dp[i-1][j-1]的基础上加1(如果不理解,在回看一下dp[i][j]的定义)

if (s[i - 1] != t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,
即:dp[i][j] = dp[i][j - 1];

其实这里 大家可以发现和 1143.最长公共子序列 (opens new window)的递推公式基本那就是一样的,区别就是 本题 如果删元素一定是字符串t,而 1143.最长公共子序列 是两个字符串都可以删元素。

dp数组如何初始化
从递推公式可以看出dp[i][j]都是依赖于dp[i - 1][j - 1] 和 dp[i][j - 1],所以dp[0][0]和dp[i][0]是一定要初始化的。

这里大家已经可以发现,在定义dp[i][j]含义的时候为什么要表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。

因为这样的定义在dp二维矩阵中可以留出初始化的区间,如图:

392.判断子序列

如果要是定义的dp[i][j]是以下标i为结尾的字符串s和以下标j为结尾的字符串t,初始化就比较麻烦了。

dp[i][0] 表示以下标i-1为结尾的字符串,与空字符串的相同子序列长度,所以为0. dp[0][j]同理。

392.判断子序列1

举例推导dp数组
以示例一为例,输入:s = "abc", t = "ahbgdc",dp状态转移图如下:

392.判断子序列2

dp[i][j]表示以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t 相同子序列的长度,所以如果dp[s.size()][t.size()] 与 字符串s的长度相同说明:s与t的最长相同子序列就是s,那么s 就是 t 的子序列。

code

lens, lent = len(s), len(t)
dp[i][j]: 0 ~ i-1的t中,0 - j-1的s中相等的元素个数
##
dp = [ [0 for _ in range(lent+1] for _ in range(lens+1]

for i in range(lens):    # 需要对s进行统计, 而不是t
    for j in range(lent):
        if s[i] == t[j]:
            dp[i+1][j+1] = dp[i][j] + 1
        else:
            dp[i+1][j+1] = dp[i+1][j]
return dp[-1][-1]


class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        lens, lent = len(s), len(t)
        dp = [ [0 for _ in range(lent+1)] for _ in range(lens+1)]
        for i in range(lens):
            for j in range(lent):
                if s[i] == t[j]:
                    dp[i+1][j+1] = dp[i][j] + 1
                else:
                    dp[i+1][j+1] = dp[i+1][j]
        return dp[-1][-1] == lens

115.不同的子序列

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE""ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。

提示:
0 <= s.length, t.length <= 1000
s 和 t 由英文字母组成

思路

# 392.判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

392 只是要求出现一次。
115 统计一共出现的次数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值