字符串序列专题
392.判断子序列
题目大意: s = “agc” t = “ahbgdc”,请判断s是不是t的子序列
注意状态转移: 当s[i-1] == t[j-1]时,显然dp[i] [j] = dp[i-1] [j-1] +1的
但是不相等的时候,当前dp状态就应该是dp[i] [j-1]时候的状态,注意s是子串,所以只需判断j-1就行
def isSubsequence2(self, s: str, t: str) -> bool:
# dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]
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]
print(dp)
if dp[-1][-1] == len(s):
return True
return False
115.不同的子序列
题目大意:s = " rabbbit" t = “rabbit”, 请找出s的子序列中t出现的个数
注意状态转移: 以s = “bagg” t ="bag"为例,s可以用s[0] s[1] s[3]来匹配 此时dp[i] [j] = dp[i-1] [j-1]
但是 也可以用s[0] s[1] s[2] 来匹配 此时dp[i] [j] = dp[i-1] [j] 用前者来匹配
lens = len(s)
lent = len(t)
dp = [[0 for _ in range(lent+1)] for _ in range(lens+1)] # 以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
for i in range(lens):
dp[i][0] = 1
for j in range(1, lent):
dp[0][j] = 0
for i in range(1, lens+1):
for j in range(1, lent+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[-1][-1]
583.两个字符串的删除操作
题目大意:给定两个单词 word1
和 word2
,返回使得 word1
和 word2
相同所需的最小步数。
word1 = “sea” word2=“eat”
注意dp表示的什么
注意状态转移: 当s[i-1] == t[j-1]时,显然dp[i] [j] = dp[i-1] [j-1]的【当前不用操作】
当s[i-1] != t[j-1]时候,就应该注意到有三种方式使得它们相同,
- 删除s和j,此时需要dp[i-1] [j-1]的状态次数+2
- 删除s最后一个,此时需要dp[i-1] [j] 的次数+1
- 删除t最后一个,此时需要dp[i] [j-1]的次数+1
三种方式取最小即可
def minDistance(self, word1: str, word2: str) -> int:
len1, len2 = len(word1), len(word2)
dp = [[0 for _ in range(len2 + 1)] for _ in range(len1 + 1)] # dp[i][j] 表示以i-1结尾和以j-1结尾需要删除的操作次数
dp[0][0] = 0
for i in range(1, len2 + 1):
dp[0][i] = i
for j in range(1, len1 + 1):
dp[j][0] = j
for i in range(1, len1 + 1):
for j in range(1, len2 + 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] + 2, dp[i - 1][j] + 1,
dp[i][j - 1] + 1) # 三种情况,删除word[i-1],word[j-1],同时删这两者
return dp[-1][-1]
72.编辑距离
题目大意:
给你两个单词 word1
和 word2
, 请返回将 word1
转换成 word2
所使用的最少操作数 。
操作数包括(增加,删除,修改)
同583类似,当word1[i-1] != word2[j-1]时
- 修改操作 dp[i] [j] = dp[i-1] [j-1] + 1
- 删除操作 dp[i-1] [ j] + 1
- 增加操作 dp[i] [j-1] + 1
def minDistance(self, word1: str, word2: str) -> int:
len1, len2 = len(word1), len(word2)
# dp[i][j] 表示以i-1, j-1结尾的字符串的最段编辑距离
dp = [[0 for _ in range(len2 + 1)] for _ in range(len1 + 1)]
for i in range(len2 + 1):
dp[0][i] = i
for j in range(1, len1 + 1):
dp[j][0] = j
# 状态转移
for i in range(1, len1 + 1):
for j in range(1, len2 + 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] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1) # 三种情况
return dp[-1][-1]
回文专题
回文最需要注意的一点就是遍历的顺序非常重要
如果我们要判断dp[i] [j]是否回文,就需要知道dp[i+1] [j-1]是否回文
所以这就要求遍历的顺序是 从下到上, 从左到右
然后求上三角矩阵的状态转移即可
647.回文子串
题目大意:给你一个字符串 s
,请你统计并返回这个字符串中 回文子串 的数目。
def countSubstrings1(self, s: str) -> int:
n = len(s)
dp = [[False for _ in range(n)] for _ in range(n)] # dp[i][j]表示i-j范围的子串是否是回文串
for i in range(n): # 初始化
dp[i][i] = True # 自身一定是回文串
res = n
# 状态转移只看上三角矩阵 注意遍历顺序:从上到下,从左到右
for j in range(1, n): # 控制列
for i in range(j): # 控制行
if s[i] != s[j]:
dp[i][j] = False
elif s[i] == s[j]:
if j - i == 1:
dp[i][j] = True
res += 1
else:
if dp[i + 1][j - 1]:
dp[i][j] = True
res += 1
return res
516.最长回文子序列
题目大意:给你一个字符串 s
,找出其中最长的回文子序列,并返回该序列的长度。
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
dp[i][i] = 1
# 注意遍历顺序, 02需要用到12,所以需要从下到上,从左到右遍历
for j in range(1, n):
for i in range(j - 1, -1, -1):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]