647. 回文子串
https://leetcode.com/problems/palindromic-substrings/
思路: 当我们看到两个下标 i, j 上 s[i] != s[j], 那么 s[i: j+1] 一定不是回文子串。 如果s[i] == s[j], 那么我们可以在检查 s[i+1: j] 是不是回文串。如果暴力搜索的话是 O(n^3) 的时间复杂度。 我们可以用一个二维 dp 数组记录s[i: j+1] 是不是回文串。 然后用 一个 visited 数组记录我们是不是已经经历过 i,j 的组合。 然后开始深度优先搜索, 如果visited[i][j] == 1, 我们直接返回 dp[i][j]. 然后如果visited[i][j] == 0, 我们碰到 s[i] != s[j], 那么 visited 数组变成 1, dp 数组填0. 如果我们碰到s[i] == s[j] 并且 j-i < 2, 那么visited 数组标记为1, dp 数组为1. 如果s[i] == s[j] 并且 j-i >= 2, 然后我们搜索 s[i+1: j]. dp[i][j] 等于搜索的返回值。
难点: 如何降低时间复杂度。
class Solution:
def countSubstrings(self, s: str) -> int:
matrix = [[0] * len(s) for _ in range(len(s))]
visited = matrix.copy()
def fds(s: str, i: int, j: int):
if visited[i][j]:
return matrix[i][j]
visited[i][j] = 1
if s[i] != s[j]:
matrix[i][j] = 0
elif s[i] == s[j] and j - i <= 1:
matrix[i][j] = 1
else:
matrix[i][j] = fds(s, i+1, j-1)
return matrix[i][j]
for i in range(0, len(s)):
for j in range(i, len(s)):
fds(s, i, j)
return sum([sum(x) for x in matrix])
516.最长回文子序列
https://leetcode.com/problems/longest-palindromic-subsequence/description/
思路: 这个问题和上一个问题挺类似的。 不同点在于回文子序列是要考虑不连续的情况。 如果是回文串, 直接用上一题的代码, 最后找出 max(j-i)。 如果是序列, 其实就是找到 s 和 s[::-1]的最长公共子序列。 不过这个问题不转换成 LCS 也可以直接解决。 考虑dp[i][j] 表示 s[i: j+1] 的最长回文子序列长度。 如果 s[i] != s[j], 那么dp[i][j] = max(dp[i+1][j], dp[i][j-1]). 如果 s[i] == s[j], 那我们再分情况, j - i < 2, dp[i][j] = j - 1 +1. 如果 j - i >= 2, dp[i][j] = dp[i+1][j-1] + 2. 这里注意递推的顺序, 所以 循环的时候 i 从 len(s-1) 到 0, j 从 i 到 len(s-1), 保证用递推关系的时候, 前置的状态已经计算过了。
难点: 注意一下循环的顺序
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
dp = [[0] * len(s) for _ in range(len(s))]
for i in range(len(s)-1, -1, -1):
for j in range(i, len(s)):
if s[i] != s[j]:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
elif s[i] == s[j] and j - i < 2:
dp[i][j] = j - i + 1
else:
dp[i][j] = dp[i+1][j-1] + 2
return dp[0][len(s)-1]