學習資料:https://programmercarl.com/0647.迴文子串.html#演算法公開課
動態規劃最後一部分:迴文字串
子串是從原字串中連續擷取的;子序列可以是從原字串中不連續提取出元素構成的
學習記錄:
647.迴文子串(難構造dp陣列,dp陣列是從原字串擷取[i,j]範圍的片段是否是迴文字串,布林變數;這樣構造才能推出遞推公式,就是當前值是二維陣列中它的左下角的值來推匯出來的)
點選檢視程式碼
class Solution(object):
def countSubstrings(self, s):
"""
:type s: str
:rtype: int
"""
# 解法一:動態規劃
# dp[i][j]代表[i,j]對應的s的片段是否為迴文子串,值為false/true
# 若首尾相等s[i]==s[j],則有三種情況s='a'則i==j;若s='aa',則i+1==j;若s='abxxxa',則j-i>1
# 構造dp二維陣列
dp = [[False]*len(s) for _ in range(len(s))]
# 初始化,略過
# 實時更新迴文子串個數
result = 0
# 開始遍歷(從下到上,從左到右)
# 難點:這裡i倒序遍歷是因為遞推公式的第二種情況,要先知道dp[i+1][j-1]再求dp[i][j],相當於從左下角推導至當前值
for i in range(len(s)-1, -1, -1): # 第二個-1代表截至0
for j in range(i, len(s)): # !!!!!!! j也就是橫座標從i開始增加,因為dp[i][j]是從i到j !!!!!
if s[i] == s[j]:
if j-i<=1: # 合併情況一、二(必然是迴文子串)
result += 1
dp[i][j] = True
elif dp[i+1][j-1]: # 情況三(此情況下,如果中間段是迴文子串,則向外擴寬1後認為迴文子串)
result += 1
dp[i][j] = True
return result
# 解法二:雙指標(略)
516.最長迴文子序列(可以不連續,所有dp陣列代表原字串在[i,j]範圍內的片段中迴文子序列的最長長度;這樣構造是為了更好的得到遞推公式)
點選檢視程式碼
class Solution(object):
def longestPalindromeSubseq(self, s):
"""
:type s: str
:rtype: int
"""
# 動態規劃,這道題迴文子序列是可以從s中不連續提取元素來構成的
# dp[i][j]代表[i,j]範圍內迴文子序列的最長長度
# 構造二維dp陣列
dp = [[0]*len(s) for _ in range(len(s))]
# 初始化,因為[i,j]範圍內找回文子序列肯定是向中間靠攏的,那最中間是遍歷不到的,需要初始化
for i in range(len(s)):
dp[i][i] = 1 # 一個字母也是一個迴文子序列,長度為1
# 開始遍歷,同理因為由左下角推導本位置,i從下到上,j從i+1開始然後從左到右遍歷
for i in range(len(s)-1, -1, -1):
for j in range(i+1, len(s)): # 前面初始化過[i][i]的情況,這裡從i+1開始
# 判斷邏輯
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2 # 在中間片段的迴文子序列基礎上加i和j對應值,就是+2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1]) # 相當於新增任意一邊的元素後,迴文子序列不變
return dp[0][len(s)-1] # 返回從0到末尾的迴文子序列的最長長度
PS:動態規劃終於學完了,好充實的題量,前面揹包那些很多沒懂,後面股票、子序列問題能聽懂,但是想不到要討論的那麼多情況。還需多刷呀。
今天天氣陰,吃了好吃的粵菜早茶,貨真價值味道也好,就是給我膩的嘞,還是淺嘗比較適合我,趕緊來了個豌雜麵緩過來了。