1 回文子串
# Given a string s, return the longest palindromic substring in s.
#
#
# Example 1:
#
#
# Input: s = "babad"
# Output: "bab"
# Explanation: "aba" is also a valid answer.
#
#
# Example 2:
#
#
# Input: s = "cbbd"
# Output: "bb"
#
#
#
# Constraints:
#
#
# 1 <= s.length <= 1000
# s consist of only digits and English letters.
#
import pandas as pd
# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
def longestPalindrome(self, s: str) -> str:
# i和j分别存储起始点和终点,数值存长度的大小
# f[i][j] = f[i+1][j-1] + 2 if s[i-1] == s[j-1] else f[i][j] = 0
n = len(s)
f = [[0] * (n) for _ in range(n)]
max_len = 0
x = -1
y = -1
for i in range(n - 1, -1, -1):
for j in range(i, n, 1):
if s[i] == s[j]:
# 初始化,回文子串的长度是奇数
if i == j:
f[i][j] = 1
else:
# 初始化,回文子串的长度是偶数
if i + 1 > j - 1:
f[i][j] = 2
# 过程更新,确保转移之前子串是回文串
elif f[i + 1][j - 1]:
f[i][j] = f[i + 1][j - 1] + 2
else:
f[i][j] = 0
if f[i][j] > max_len:
x = i
y = j
max_len = f[i][j]
return s[x:y + 1]
# leetcode submit region end(Prohibit modification and deletion)
if __name__ == '__main__':
so = Solution()
s = "babad"
res = so.longestPalindrome(s)
print(res)
1.1 解题思路
- 确定
f[i][j]
的含义,表示以i
子串开始,j
子串结束,即f[i][j]
回文子串的长度。 - 确定动态转移方程,动态转移方程的核心
f[i][j] = f[i + 1][j - 1] + 2 if s[i] == s[j]
。 - 确定初始化条件,初始化条件包括
奇数和偶数
两种情况。 - 确定遍历的顺序,
i
依赖i+1
,j
依赖j-1
,所以对i
进行倒序遍历
,对j
进行正序遍历
。
2 回文子序列
# Given a string s, find the longest palindromic subsequence's length in s.
#
# A subsequence is a sequence that can be derived from another sequence by
# deleting some or no elements without changing the order of the remaining elements.
#
#
# Example 1:
#
#
# Input: s = "bbbab"
# Output: 4
# Explanation: One possible longest palindromic subsequence is "bbbb".
#
#
# Example 2:
#
#
# Input: s = "cbbd"
# Output: 2
# Explanation: One possible longest palindromic subsequence is "bb".
#
#
#
# Constraints:
#
#
# 1 <= s.length <= 1000
# s consists only of lowercase English letters.
#
# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
# f[i][j] = f[i+1][j-1] + 2 if s[i]==s[j]
# f[i][j] = max(f[i][j-1], f[i-1][j]) if s[i] != s[j]
l = len(s)
max_len = 0
f = [[0] * l for _ in range(l)]
for i in range(l - 1, -1, -1):
for j in range(i, l, 1):
if s[i] == s[j]:
if i == j:
f[i][j] = 1
else:
if i + 1 > j - 1:
f[i][j] = 2
else:
f[i][j] = f[i + 1][j - 1] + 2
else:
f[i][j] = max(f[i][j - 1], f[i + 1][j])
if f[i][j] > max_len:
max_len = f[i][j]
return max_len
# leetcode submit region end(Prohibit modification and deletion)
if __name__ == '__main__':
so = Solution()
s = "bbbab"
res = so.longestPalindromeSubseq(s)
print(res)
2.1 解题思路
- 回文子串和回文子序列关键差异在于动态转移方程不相等情况的处理,回文子串如果不相等,
f[i][j] = 0
,回文子序列则是f[i][j] = max(f[i][j - 1], f[i + 1][j])
3. 最长公共子序列
3.1 题意
3.2 代码
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i - 1] == text2[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 dp[m][n]
公共子序列需要从同时考虑左上[i - 1][j - 1]
、左边[i - 1][j]
、上边[i][j - 1]
三个方向,如下代码:
if text1[i - 1] == text2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
4. 最长公共子串
4.1 题意
4.2 代码
class Solution:
def findLength(self, A: List[int], B: List[int]) -> int:
n, m = len(A), len(B)
dp = [[0] * (m + 1) for _ in range(n + 1)]
ans = 0
for i in range(n - 1, -1, -1):
for j in range(m - 1, -1, -1):
dp[i][j] = dp[i + 1][j + 1] + 1 if A[i] == B[j] else 0
ans = max(ans, dp[i][j])
return ans
公共子串只需要从同时考虑左上[i - 1][j - 1]
一个方向,如下代码:
dp[i][j] = dp[i + 1][j + 1] + 1 if A[i] == B[j] else 0
5. 编辑距离
5.1 题意
5.2 代码
class Solution:
def minDistance(self, word1: str, word2: str) -> 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
up = D[i][j - 1] + 1
left_up = D[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
left_up += 1
D[i][j] = min(left, up, left_up)
return D[n][m]
类似最长公共子序列,需要同时考虑左边left ,上面up ,左上left_up三个方向,代码如下:
left = D[i - 1][j] + 1
up = D[i][j - 1] + 1
left_up = D[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
left_up += 1
D[i][j] = min(left, up, left_up)