53. 最大子序和
贪心:
class Solution:
def maxSubArray(self, nums):
result = float('-inf') # 初始化结果为负无穷大
count = 0
for i in range(len(nums)):
count += nums[i]
if count > result: # 取区间累计的最大值(相当于不断确定最大子序终止位置)
result = count
if count <= 0: # 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
count = 0
return result
动态规划:
dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]。
class Solution:
def maxSubArray(self, nums):
dp = [0] * len(nums)
dp[0] = nums[0]
result = nums[0]
for i in range(1, len(nums)):
dp[i] = max(dp[i-1] + nums[i], nums[i])
result = max(result, dp[i])
return result
392.判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
我的解法:
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
count = len(s)
j = 0
for i in range(len(t)):
if j == len(s):
return True
if t[i] == s[j]:
j += 1
return True if j == len(s) else False
动态规划:
同最长公共子序列,最后判断最长公共子序列长度是否等于len(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
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];
区别就是 本题 如果删元素一定是字符串t,因为这里s是一定要匹配的,而 1143.最长公共子序列 是两个字符串都可以删元素。
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
dp = [[0]* (len(t)+1) for _ in range(len(s) + 1)]
result = 0
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]
result = max(result, dp[i][j])
return result == len(s)
115.不同的子序列
给你两个字符串 s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数,结果需要对 109 + 7 取模。
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)):
dp[i][0] = 1
for j in range(1, len(t)):
dp[0][j] = 0
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[-1][-1]
583. 两个字符串的删除操作
给定两个单词 word1
和 word2
,返回使得 word1
和 word2
相同所需的最小步数。
每步 可以删除任意一个字符串中的一个字符。
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
result = 0
dp = [[0]*(len(word2)+1) for _ in range(len(word1) + 1)]
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] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return len(word1) + len(word2) - 2*dp[-1][-1]
72. 编辑距离
给你两个单词 word1
和 word2
, 请返回将 word1
转换成 word2
所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
dp = [[0] * (len(word2)+1) for _ in range(len(word1)+1)]
# dp[i][j]表示包含world1[i-1]和包含world2[j-1]的字符串的编辑距离
for i in range(1, len(word1) + 1):
dp[i][0] = i
for j in range(1, len(word2) + 1):
dp[0][j] = j
# dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;同理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:
# 1.插入 可以转换成删除
# world1插入 = world2删除
# eg:
# word1 = "a" ,word2 = "ad",word1插入元素'd'
# word1 = "a" ,word2 = "ad",word2删除元素'd'
# 删除world1[j-1]:dp[i][j] = dp[i][j-1] + 1
# 2.删除:
# 删除world1[i-1]: dp[i][j] = dp[i-1][j] + 1
# 3.替换:
# dp[i][j] = dp[i-1][j-1] + 1
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
return dp[-1][-1]
647. 回文子串
给你一个字符串 s
,请你统计并返回这个字符串中 回文子串 的数目。
回文字符串 是正着读和倒过来读一样的字符串。
子字符串 是字符串中的由连续字符组成的一个序列。
我的解法:超出时间限制
class Solution:
def countSubstrings(self, s: str) -> int:
dp = [0] * len(s)
# 初始化第一个字符为回文子串
dp[0] = 1
for i in range(1, len(s)):
# 从当前位置向前检查所有可能的回文子串
for j in range(i, -1, -1):
if self.is_huiwen(s[j:i+1]):
dp[i] += 1
return sum(dp) # 返回所有回文子串的总数
def is_huiwen(self, s: str) -> bool:
p = 0
q = len(s) - 1
while p < q:
if s[p] != s[q]:
return False
p += 1
q -= 1
return True
动态规划解法:
难点:依旧是构建动态规划数组
class Solution:
def countSubstrings(self, s: str) -> int:
dp = [[False]*len(s) for _ in range(len(s))]
result = 0
for i in range(len(s)-1, -1, -1):
for j in range(i, len(s)):
if s[i] == s[j]:
if j-i <= 1 or dp[i+1][j-1]:
result += 1
dp[i][j] = True
return result
双指针递归解法:
class Solution:
def countSubstrings(self, s: str) -> int:
result = 0
for i in range(len(s)):
result += self.count(s, i, i)
result += self.count(s, i, i+1)
return result
def count(self, s, i, j):
result = 0
while i >= 0 and j < len(s) and s[i]==s[j]:
i -= 1
j += 1
result += 1
return result
516.最长回文子序列
给你一个字符串 s
,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例 1:
输入:s = "bbbab" 输出:4 解释:一个可能的最长回文子序列为 "bbbb" 。
注意:这里是子序列,不是子串!
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
# a b a a c a
# a 1 1 3 3 3 4
# b 0 1 1 2 2 3
# a 0 0 1 2 2 3
# a 0 0 0 1 1 3
# c 0 0 0 0 1 1
# a 0 0 0 0 0 1
dp = [[0]*len(s) for _ in range(len(s))]
for i in range(len(s)):
dp[i][i] = 1
for i in range(len(s)-1, -1, -1):
for j in range(i+1, len(s)):
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][-1]