python 判断子序列_LeetCode 392. 判断子序列 | Python
392. 判斷子序列
題目
給定字符串 s 和 t ,判斷 s 是否為 t 的子序列。
你可以認為 s 和 t 中僅包含英文小寫字母。字符串 t 可能會很長(長度 ~= 500,000),而 s 是個短字符串(長度 <=100)。
字符串的一個子序列是原始字符串刪除一些(也可以不刪除)字符而不改變剩余字符相對位置形成的新字符串。(例如,"ace"是"abcde"的一個子序列,而"aec"不是)。
示例 1:
s = "abc", t = "ahbgdc"
返回 true.
示例 2:
s = "axc", t = "ahbgdc"
返回 false.
后續挑戰 :
如果有大量輸入的 S,稱作S1, S2, ... , Sk 其中 k >= 10億,你需要依次檢查它們是否為 T 的子序列。在這種情況下,你會怎樣改變代碼?
解題思路
思路:雙指針、動態規劃
在這里,先理清題目所提出的問題,題目要問的是,s 是否是 t 的子序列?而題目中定義這個子序列,是指不改變相對位置在原字符串中刪除一些(或者不刪除)字符剩余的字符。
那么也就是說,只要能找到 s 在 t 中存在(相對位置順序對應),那么可以認定 s 是 t 的子序列。例如,題目中所給出的示例,"ace" 是 "abcde" 的一個子序列,而 "aec" 不是。因為 "aec" 改變了相對位置的順序。
在這里,我們可以從前往后匹配,而且可貪心地靠前匹配出現的字符。
當我們從前往后匹配字符的時候,假設出現的字符 x 在 t 中出現的位置,一個在前面,一個在后面。在這里,應該考慮匹配 x 在 t 出現前面的字符,這是因為往后匹配,當選定前面位置出現的字符時,能夠更大概率匹配成功。(因為字符 x 出現在后面位置往后能取的字符,前面位置往后也能夠取到,而且前后兩個位置之前的字符也有可選字符。)
那么具體的算法如下:
定義雙指針 p、q,分別指向 s 和 t 的初始位置;
這里匹配前面位置出現的字符(也就是進行貪心匹配),當匹配成功之后,指針同時往后移動;
如果匹配失敗,p 保持不同,移動 q。
如果 p 能夠到達末尾,那么說明 s 就是 t 的子序列。
具體的代碼見【代碼實現 # 雙指針】
還有一個后續挑戰,需要檢驗大量的 s 是否是 t 的子序列。在上面的雙指針的方法當中,從前往后去匹配字符需要大量的時間,那么這里再使用雙指針的方法顯然不合適。
這里參考官方題解,說一下動態規劃如何去快速匹配 s 是否是 t 的子序列。
首先用動態規劃的方法去進行預處理,能夠確定在 t 的每個位置,從該位置往后每個字符第一次出現的位置。
狀態定義
設 dp[i][j] 表示字符串 t 中從 i 的位置開始往后匹配,j 第一次出現的位置。
狀態轉移方程
如果 t 中位置 i 的字符就是 j 的話,那么 dp[i][j] = i;
若不是上面的情況,那么也就是說 j 出現在 i 位置之后的某個位置(這里不包含 i),此時 dp[i][j] = dp[i+1][j]
狀態初始化
在這里,索引從 0 開始,那么 i 的取值范圍為 [0, t_len),這里不包含 t_len。那么,這里存在邊界問題,當 i = t_len-1 的時候,這里可能會無法進行轉移。我們讓 i = t_len 的時候,令 dp[t_len][...] 為 t_len,那么也就說,當 dp[i][j] = t_len 的時候,那么就表示從 i 開始無法匹配 j。
具體的代碼見【代碼實現 # 動態規劃】
代碼實現
# 雙指針
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
s_len = len(s)
t_len = len(t)
# 定義雙指針,指向 s 和 t 的初始位置
p = 0
q = 0
while p < s_len and q < t_len:
# 當 s 的字符與 t 的字符匹配時
# 同時移動 p 和 q 指針
if s[p] == t[q]:
p += 1
# 如果不匹配,只移動 q 指針,與 p 指針所對應的字符繼續匹配判斷
q += 1
# 如果 p 指針到達 s 末尾返回 True
return p == s_len
# 動態規劃
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
s_len = len(s)
t_len = len(t)
dp = [[0] * 26 for _ in range(t_len)]
# 這里是為了能夠讓 i = t_len-1 的時候能夠正常轉移
dp.append([t_len]*26)
# 在這里,從后往前枚舉,因為 dp[i][j] 可能從 dp[i+1][j] 中轉移而來
for i in range(t_len-1, -1, -1):
for j in range(26):
# 如果位置 i 的字符就是 j 時,那么 dp[i][j] = i
if ord(t[i]) == j + ord('a'):
dp[i][j] = i
else:
dp[i][j] = dp[i+1][j]
# dp[i][j] = i if ord(t[i]) == j + ord('a') else dp[i+1][j]
# 開始遍歷匹配 s,檢驗 s 的每個字符在 t 中的某個位置是否存在
idx = 0
for i in range(s_len):
# 如果轉移只有結果為 t_len,表示無法匹配字符,那么返回 False
if dp[idx][ord(s[i]) - ord('a')] == t_len:
return False
# 當找到匹配當前字符的位置之后,從這個位置的下一個位置開始查找下一個字符是否出現在 t 中的某個位置
idx = dp[idx][ord(s[i]) - ord('a')] + 1
return True
實現結果
雙指針
動態規劃
歡迎關注
公眾號 【書所集錄】
總結
以上是生活随笔為你收集整理的python 判断子序列_LeetCode 392. 判断子序列 | Python的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在苹果Mac电脑中如何将键盘当作鼠标使用
- 下一篇: 手机无线投屏到电脑手机如何无线投屏电脑