647. 迴文子串
題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/palindromic-substrings python
題目
給定一個字符串,你的任務是計算這個字符串中有多少個迴文子串。算法
具備不一樣開始位置或結束位置的子串,即便是由相同的字符組成,也會被視做不一樣的子串。數組
示例 1:bash
輸入:"abc" 輸出:3 解釋:三個迴文子串: "a", "b", "c"
示例 2:ui
輸入:"aaa" 輸出:6 解釋:6個迴文子串: "a", "a", "a", "aa", "aa", "aaa"
提示:.net
- 輸入的字符串長度不會超過 1000 。
解題思路
思路:動態規劃
先看題目,題目要求在給定的字符串中,求得字符串中有多少個迴文子串。其中說起,不一樣開始或結束位置的子串,即使相同也視爲不一樣子串。code
其實看完題目,咱們想到最直接的想法就是,先枚舉字符的組合,判斷這些字符組合成的子串是不是迴文串便可。blog
如今咱們來看看,用這種直接的方法代碼實現:leetcode
class Solution: def countSubstrings(self, s: str) -> int: def is_palindrome(string): """判斷傳入字符串是不是迴文串 """ left = 0 right = len(string) - 1 while left < right: if string[left] != string[right]: return False left += 1 right -= 1 return True # 計數 count = 0 # 枚舉字符組合 for i in range(len(s)): for j in range(i, len(s)): # 判斷字符組合是不是迴文串 # 如果計數 +1,不然跳過 sub_string = s[i:j+1] if is_palindrome(sub_string): count += 1 return count
上面的方法中,假設字符串長度爲 n,咱們枚舉全部子串須要 $O(n^2)$ 的時間,而判斷子串是否迴文串須要 $O(S)$ 的時間,S 是子串的長度,因此整個算法的時間是 $O(n^3)$。字符串
這裏用 Python 執行結果超時,也側面說明思路是可行的。這裏執行超時的緣由如上所述,是由於頻繁對字符串切片以及判斷子串是不是迴文串。
下面咱們看看使用動態規劃的思路如何解決。
動態規劃
假設,s[i...j]
(i...j 表示這個區間內的字符包含 i、j)是迴文串。那麼 s[i-1...j+1] 只有在 s[i-1] == s[j+1] 的狀況下,纔是迴文串。
狀態定義
如今設 dp[i][j]
表示 s[i...j]
是不是迴文串。
狀態轉移方程
接下來,咱們分析一下,子串是迴文串成立的狀況:
- 若是 i == j,那麼表示是單字符,單字符也是迴文串;
- 若是
s[i] == s[j]
且i+1=j
(或i=j-1
),那麼這裏表示兩個字符且相同,那麼一樣是迴文串; - 若是
dp[i+1][j-1] == True
,也就是 s[i+1...j-1] 是迴文串時,若s[i]==s[j]
,此時dp[i][j]
一樣也是迴文串。
咱們能夠看到,第2、三種狀況是能夠合併在一塊兒的。
當 s[i]==s[j]
,只要 i==j-1
或者 dp[i+1][j-1]==True
其中一個成立,dp[i][j]
都爲 True
,s[i...j]
是迴文串。公式以下:
$dp[i][j] = True, \qquad if , (s[i] == s[j]) , and , (i==j-1 , or , dp[i+1][j-1])$
再看第一種狀況,咱們發現,其實 i==j 時,s[i] == s[j] 也是成立的,只是此時 i=j-0
,。
那麼這裏再將第一種狀況跟上面合併,也就是 i >= j - 1 或者 i - j >= -1 時,公式以下:
$dp[i][j] = True, \qquad if , (s[i] == s[j]) , and , (i-j>=-1 , or , dp[i+1][j-1])$
複雜度分析:
- 時間複雜度: $O(n^2)$
- 空間複雜度: $O(n^2)$, dp 數組的開銷。
還有 中心擴散法,這個方法可以將空間複雜度下降爲常數時間複雜度 $O(1)$。這裏在官方題解有給出詳細內容,有興趣的能夠從下面連接入口進入瞭解。
具體的代碼實現以下。
代碼實現
class Solution: def countSubstrings(self, s: str) -> int: # 計數 count = 0 n = len(s) # 定義 dp 數組,初始化爲 False dp = [[False] * n for _ in range(n)] # 咱們從右往左遍歷,填充 dp 數組 for i in range(n-1, -1, -1): for j in range(i, n): # 根據文章得出的狀態轉移方程 if s[i]==s[j] and (i-j>=-1 or dp[i+1][j-1]): dp[i][j] = True count += 1 return count
實現結果
歡迎關注
公衆號 【書所集錄】