392.判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:
- 输入:s = "abc", t = "ahbgdc"
- 输出:true
这个题是一个简单题,可以用双指针,也可以动态规划,也是入门编辑距离的入门题,只涉及删除操作。
双指针
一个指针指向s 一个指针指向t t指针一个移动,s指针如果遇到相等就移动
如果s指针能移动到末尾,说明就可以了
def isSubsequence(self, s: str, t: str) -> bool:
if len(s) == 0:
return True
point1 = 0
for point2 in range(len(t)):
if s[point1] == t[point2]:
point1 += 1
if point1 >= len(s):
return True
return False
动态规划
1. dp数组定义
使用二维dp数组
dp[i][j] 代表 0-i-1 的s 在0-j-1的子序列出现的最大长度
2. dp递推公式
如果相等
dp[i][j] = dp[i-1][j-1] + 1
如果不等
dp[i][j] = dp[i][j-1] # 删除字符t
3. dp数组初始化
dp[i][0] = 0
dp[0]][0]
4. 遍历顺序
从递推公式可以看出,递推的方式是从左到右 从上倒下的
5.
def isSubsequence(self, s: str, t: str) -> bool:
dp = [[0] * (len(t) + 1) for _ in range(len(s) + 1)]
for i in range(1, len(s) + 1):
for j in range(1, len(t) + 1):
if s[i-1] == t[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = dp[i][j-1]
if dp[-1][-1] == len(s):
return True
return False
115.不同的子序列
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。
这个题跟上面的题不同的时,你需要统计了个数了,同样也是子序列匹配的问题
数组定义是一样的,但是删除的话,我们也是指对s进行删除
核心变化在递推公式
如果相等,也就是说我们可以直接加上前面i-2和j-2的字符中匹配的,就用s[i-1]来进行匹配
,只需要dp[i-1][j-1]。但我们也可以不用s[i-1]来匹配,个数为dp[i-1][j]
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
如果不相等
dp[i][j] = dp[i-1][j] 不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j]
之后是dp数组的初始化
dp[i][0] = 1 为什么? 以i-1为结尾的s可以随便删除元素,出现空字符串的个数。
那么dp[0][j]一定都是0,s如论如何也变成不了t。
dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串t。
class Solution:
def numDistinct(self, s: str, t: str) -> int:
dp = [[0] * (len(t) + 1) for _ in range(len(s) + 1)]
for i in range(len(s) + 1):
dp[i][0] = 1
dp[0][0] = 1
for i in range(1, len(s) + 1):
for j in range(1, len(t) + 1):
if s[i-1] == t[j-1]:
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
else:
dp[i][j] = dp[i-1][j]
return dp[len(s)][len(t)]
583. 两个字符串的删除操作
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例:
- 输入: "sea", "eat"
- 输出: 2
- 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
这个题又是类似匹配,但是只有删除操作
- 确定dp数组(dp table)以及下标的含义
dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
2. dp递推公式
如果遇到字符相等
那么 dp[i][j] = dp[i-1][j-1] 不需要进行操作
如果字符不相等
情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2
要么删除i 要么删除j 要么删除i和j
dp[i][j] = min(dp[i-1][j-1] + 2, dp[i-1][j] + 1, dp[i][j-1] + 1)
3. dp数组初始化
dp[0][j] = j 删除j次
dp[i][0] = i 删除i次
dp[0][0] = 0
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
dp = [[0] * (len(word2) + 1) for _ in range(len(word1) + 1)]
for i in range(len(word1) + 1):
dp[i][0] = i
for j in range(len(word2) + 1):
dp[0][j] = j
for i in range(1, len(word1) + 1):
for j in range(1, len(word2) + 1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1] + 2)
return dp[-1][-1]
72. 编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
-
插入一个字符
-
删除一个字符
-
替换一个字符
-
示例 1:
-
输入:word1 = "horse", word2 = "ros"
-
输出:3
-
解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')
-
示例 2:
-
输入:word1 = "intention", word2 = "execution"
-
输出:5
-
解释: intention -> inention (删除 't') inention -> enention (将 'i' 替换为 'e') enention -> exention (将 'n' 替换为 'x') exention -> exection (将 'n' 替换为 'c') exection -> execution (插入 'u')
和删除操作很像,但是添加、删除和替换的理解挺磨人的
1. dp数组定义
dp[i][j] 是指从0-i-1 的word1字符转换为0-j-1word字符使用的最少操作次数为dp[i][j]
2. dp递推公式
如果相等,那就不用编辑
dp[i][j] = dp[i-1][j-1]
如果不相等
1. 删除操作:删除i 或者删除j dp[i-1][j] + 1 dp[i][j-1] + 1
2. 添加操作,其实就是删除 你删除i可以理解为添加j
3. 替换操作:word1
替换word1[i - 1]
,使其与word2[j - 1]
相同,此时不用增删加元素。
不等嘛,我们把不等的替换成等的, 如果相等,此时dp[i][j]等于dp[i-1][j-1],但是我们替换了,因此还要+1.dp[i][j] = dp[i-1][j-1] + 1
这三种操作,我们要选最小的
3. dp数组初始化
dp[0][j] = j 删除j次
dp[i][0] = i 删除i次
结束
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
dp = [[0] * (len(word2) + 1) for _ in range(len(word1) + 1)]
for i in range(len(word1) + 1):
dp[i][0] = i
for j in range(len(word2) + 1):
dp[0][j] = j
for i in range(1, len(word1) + 1):
for j in range(1, len(word2) + 1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
return dp[len(word1)][len(word2)]