- 最长回文字串
一、第一种解决方案是通过暴力求解
首先我们列出来该字符串所有的字串然后进行判断是否是回文串,返回长度最长的那个为结果。
"""
5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
"""
class Solution1(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s)<2:
return s
max_len = 1
res = s[0]
for i in range(len(s) - 1):
for j in range(i + 1, len(s)):
if j - i + 1 > max_len and s[i:j+1] == s[i:j+1][::-1]:
max_len = j - i + 1
res = s[i:j + 1]
return res
二、 通过动态规划解决该问题
通过分析知如果我们想判断一个字符串中第i个位置到第j个文字的字串是否为回文串我们可以将其转化为判断:
dp[i][j] = s[i] == s[j] and dp[i+1][j-1]
上边状态转移方程的意思是 判断i-j这个字符串是否是回文串和判断i+1,j-1这个字串和第i和j这两个字符相等是等价的问题。
同时需要满足 字串是大于3的菜执行上述方程。
"""
设计状态转移方程
dp[i][j] = s[i] == s[j] and dp[i+1][j-1]
"""
class Solution2:
def longestPalindrome(self, s):
n = len(s)
dp = [[False] * n for _ in range(n)]
ans = ""
# 枚举子串的长度 l+1
for l in range(n):
# 枚举子串的起始位置 i,这样可以通过 j=i+l 得到子串的结束位置
for i in range(n):
j = i + l
# 结束位置大于 字符串长度 break
if j >= len(s):
break
# 长度为1的字符串都为 回文字串 因此 为True
if l == 0:
dp[i][j] = True
# 长度为2的字符串 判断这两个字符是否相等 判断回文字串
elif l == 1:
dp[i][j] = (s[i] == s[j])
# 长度为3以上的字符串 判断
else:
dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
if dp[i][j] and l + 1 > len(ans):
ans = s[i:j+1]
return ans
- 编辑距离
编辑距离在判断机器翻译性能时候常用.两个单词的编辑距离为X,意味着单词1变成了单词2,需要X步,并且这是个最少的步数。
"""
72. 编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
"""
"""
我们有word1和word2,我们定义dp[i][j]的含义为:word1的前i个字符和word2的前j个字符的编辑距离。意思就是word1的前i个字符,变成word2的前j个字符,最少需要这么多步。
知道"nfch"变成"fgh"多少步(假设X步),那么从"abcde"到"fgh"就是"abcde"->"abcd"->"fgh"。(一次删除,加X步,总共X+1步)
知道"nfch"变成“fg”多少步(假设Y步),那么从"abcde"到"fgh"就是"abcde"->"fg"->"fgh"。(先Y步,再一次添加,加X步,总共Y+1步)
知道"nfch"变成“fg”多少步(假设Z步),那么从"abcde"到"fgh"就是"abcde"->"fge"->"fgh"。(先不管最后一个字符,把前面的先变好,用了Z步,然后把最后一个字符给替换了。这里如果最后一个字符碰巧就一样,那就不用替换,省了一步)
以上三种方式算出来选最少的,就是答案。
dp[i][j]=min(dp[i-1][j]+1,dp[i][j+1]+1,dp[i][j]+int(word1[i]!=word2[j]))
dp[i-1][j]:情况一
dp[i][j-1]+1:情况二
dp[i-1][j-1]+int(word1[i]!=word2[j]):情况三
"""
# 动态规划法
class Solution3:
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
n = len(word1)
m = len(word2)
# 有一个字符串为空串
if n * m == 0:
return n + m
# DP 数组
D = [[0] * (m + 1) for _ in range(n + 1)]
# 边界状态初始化
for i in range(n + 1):
D[i][0] = i
for j in range(m + 1):
D[0][j] = j
# 计算所有 DP 值
for i in range(1, n + 1):
for j in range(1, m + 1):
left = D[i - 1][j] + 1
down = D[i][j - 1] + 1
left_down = D[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
left_down += 1
D[i][j] = min(left, down, left_down)
return D[n][m]
- 打家劫舍问题1
# 使用数组记录每一个位置记录第k个房间的最大获利金额。
"""
198. 打家劫舍
设数组为nums
S0为第一个位置能获取的最大金额。 S0 = nums[0]
S1为第二个位置能获取的最大金额。 S1 = max(S0,nums[1])
S2为第三个位置能获取的最大金额。 S2 = max(S1,S[0]+nums[i])
S3为第四个位置能获取的最大金额。 S3 = max(S2,S[1]+nums[i])
Sn = max(Sn-1,Sn-2+nums[i])
第一种方法 是 初始化一个和nums大小相同的数据存放第k个房间最大的获利金额。
第二种方法 是 仅用两个变量存待计算房间的前两个的最大获利金额。
"""
class Solution4:
def rob(self, nums):
if not nums:
return 0
size = len(nums)
if size == 1:
return nums[0]
dp = [0] * size
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, size):
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
return dp[size - 1]
class Solution5:
def rob(self, nums):
if not nums:
return 0
size = len(nums)
if size == 1:
return nums[0]
first, second = nums[0], max(nums[0], nums[1])
for i in range(2, size):
first, second = second, max(first + nums[i], second)
return second
- 打家劫舍问题2
该问题是打家劫舍问题1的改进只是将问题1中的房间顺序有顺序列转化为环排列。也就是首尾衔接。
在解决该问题是在问题1的基础上需要加一个条件也就是首尾不能相见。
设每个房间的金币数存在nums种我们只需要通过nums[:-1]和nums[1:]这两个序列就可以求出最大的获利金额。
class Solution7:
def rob(self, nums):
def rob_(nums):
size = len(nums)
first, second = 0,0
for i in range(0, size):
first, second = second, max(first + nums[i], second)
return second
return max(rob_(nums[:-1]),rob_(nums[1:])) if len(nums) != 1 else nums[0]
- 最长回文子序列
子序列和子串不一样,子序列可以非连续。
# 最长回文子序列
"""
516. 最长回文子序列
1. 求出状态转移方程
如果 s 的第 i 个字符和第 j 个字符相同的话
dp[i][j] = dp[i + 1][j - 1] + 2
如果 s 的第 i 个字符和第 j 个字符不同的话
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
2. 设置对角全部为1 从下往上开始计算
ij0 1 2 3 4
0 1 依次向上 执行 求 dp[0][1] dp[0][2] dp[0][3] dp[0][4]
1 1 依次向上 执行 求 dp[1][2] dp[1][3] dp[1][4]
2 1 依次向上 执行 求 dp[2][3] dp[2][4]
3 1 从这里开始遍历计算两种情况 dp[3][4] = dp[i][j] = dp[i + 1][j - 1] + 2 dp[3,4] = max(dp[i][j - 1], dp[i + 1][j])
4 1
3. 求出 最大子序列
"""
class Solution8:
def longestPalindromeSubseq(self, s):
n = len(s)
maxL = -1
dp = [[0] * n for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i][j - 1], dp[i + 1][j])
return dp[0][n - 1]
- 最长连续递增子序列
一、第一种解决方案是通过暴力求解:
该问题的核心在于找到递增连续的序列,假如第i个元素小于第i+1个元素我们可以判断由这两个元素构成的序列满足递增连续子序列条件。因此如果满足上述则记录子序列长度。如果不满足从不满足的位置重新开始判断求新的子序列。
# 674. 最长连续递增序列
"""
给定一个未经排序的整数数组,找到最长且连续的的递增序列,并返回该序列的长度。
示例 1:
输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。
"""
class Solution9:
def findLengthOfLCIS(self, nums):
"""遍历一遍就解决问题"""
n = len(nums)
if n <= 1:
return n
count = 1
res = 1
for i in range(1, n):
if nums[i] > nums[i - 1]:
count += 1
else:
count = 1
res = max(res, count)
return res
二、 通过动态规划解决该问题
该问题的核心找到状态转移方程将问题转为多个相关的子问题,其实第i个位置的能够构成的递增序列仅与第i-1个位置有关。分为两种情况如果第i个元素大于第i-1个元素则递增子序列长度:dp[i] = dp[i-1] + 1。否则: dp[i] = 1。
class Solution10:
def findLengthOfLCIS(self, nums):
"""动态规划"""
n = len(nums)
if n == 0:
return 0
dp = [0] * n
dp[0] = 1
for i in range(1, n):
if nums[i] > nums[i-1]:
dp[i] = dp[i-1] + 1
else:
dp[i] = 1
return max(dp)