[演算法] 最長迴文串
阿新 • • 發佈:2022-02-14
最長子迴文串
思路一
使用動態規劃來處理這樣的題目,令一個二維表格\(dp\)記錄結果,其中\(dp[i][j]\)記錄的字串中區間\([i,j]\)之間的最大回文串的長度,這樣我們可以發現初始值:\(dp[i][i]=1\),且在\(i>j\)的情況下均為0,同時,有如下轉移公式:
\[\begin{align} &s[i]=[j]: dp[i][j]=dp[i+1][j-1]+2\\ &s[j]\neq s[j]:dp[i][j]=max(dp[i+1][j],dp[i][j-1]) \end{align} \]於是可以寫出如下程式碼:
def method1(s: str) -> int: # 第一種思路,動態規劃,記錄dp[i][j]記錄[i,j]之間的最大回文串,i==j時:1,i>j時:0 # 判斷s[i]==s[j]則,dp[i][j]=dp[i+1][j-1]+2,否則dp[i][j]=max(dp[i+1][j],dp[i][j-1]) # 由此,需要確定,i倒著遍歷,j正著遍歷 n = len(s) dp = [[0] * n for _ in range(n)] for i in range(n): dp[i][i] = 1 i = n - 1 while i >= 0: for j in range(i + 1, n): if s[i] == s[j]: dp[i][j] = dp[i + 1][j - 1] + 2 else: # 這裡解釋一下索引,i+1一定小於等於j小於n,j-1一定大於等於i大於0 dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) i -= 1 return dp[0][n - 1]
上述方法時間複雜度為\(o(n^2)\),空間複雜度為\(o(n^2)\)。
考慮優化一下空間,仔細觀察轉移公式,可以發現迭代過程中,是從最後一行向上迭代,並且迭代每一行的時候,僅依賴於下一行,於是可以考慮使用單獨的一個數組來替代整個的二位陣列,有如下程式碼:
def method2(self, s: str) -> int: # 優化一下空間,僅採用兩個向量來記錄結果動態規劃結果 n = len(s) dp = [0] * n for i in range(n - 1, -1, -1): tem_dp = [0] * n tem_dp[i] = 1 for j in range(i + 1, n): if s[i] == s[j]: tem_dp[j] = dp[j - 1] + 2 else: tem_dp[j] = max(dp[j], tem_dp[j - 1]) dp = tem_dp return dp[n - 1]
和方法一唯一的不同點在於,僅記錄了原本二維陣列中最新的一行的資料,於是,在時間複雜度不變的情況下,空間複雜度優化到了\(o(n)\);
思路二
將字串做一個反轉後,求兩個字串的最大子序列,這樣就等價於求最長迴文序列;
字串沒有必要實際的進行反轉,僅在遍歷過程中選取對應的字元即可;
程式碼如下:
def method3(self, s: str) -> int: # 另一個思路,將字串反轉,然後求解兩個串的最長子序列 n = len(s) dp = [[0] * (n + 1) for _ in range(n + 1)] for i in range(1, n + 1): for j in range(1, n + 1): if s[- j] == s[i - 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[n][n]
求解最長公共子序列是個經典問題,依舊是採用的動態規劃的思路求解,\(dp[i][j]\)記錄的是兩個字串的字串\([0:i+1]\)和\([0:j+1]\)的最長字串長度;
演算法的時間複雜度是\(o(n^2)\),空間複雜度也是\(o(n^2)\),但是由於這裡遍歷的時候要將整個二維陣列全部更新一遍,二思路一則僅僅更新了一半的矩陣,因此實際執行時,思路1是要比這個思路快的;
同樣的,從轉移方程可以看出,更新新一行資料時僅依賴上一行的資料,於是我們可以優化下空間複雜度到\(o(n)\),程式碼如下:
def method4(self, s: str) -> int:
# 順著求最大字串的思路,優化一下空間
n = len(s)
dp = [0] * n
for i in range(0, n):
temp = [0] * n
for j in range(0, n):
if s[n - j - 1] == s[i]:
temp[j] = dp[j - 1] + 1 if j > 0 else 1
else:
temp[j] = max(dp[j], temp[j - 1] if j > 0 else 0)
dp = temp
return dp[n - 1]
學習java的新手,嘗試將學到的內容總結為部落格,內容如有錯誤,歡迎指正,感激不盡。