求迴文串方法小結
記錄一下在一個字串中求迴文串的三種常用的方法:
練手題目:https://leetcode-cn.com/problems/palindromic-substrings/
方法一:這個也是我們一般人最容易想到的,就是直接列舉左右端點,然後去判斷兩個端點內的字串是不是迴文串就行了,時間複雜度O(n^3)
class Solution { public: string S; bool check(int left,int right){ while (left < right){ if (S[left] == S[right]){ left++; right--; }else return false; } return true; } int countSubstrings(string s) { S = s; int n = s.size(),ans = 0; for (int i = 0; i < n; i++){ for (int j = i; j < n; j++){ if (check(i,j)) ans++;//定左右端點並check一下 } } return ans; } };
方法二(1):只要是迴文串,那麼它肯定有迴文中心。這樣我們第二個思路也就出來了,去列舉迴文中心,然後不斷往兩邊去拓展,具體步驟結合題目看程式碼,時間複雜度O(n^2)
class Solution { public: int countSubstrings(string s) { int n = s.size(),ans = 0; for (int i = 0; i < 2*n-1; i++){//迴文中心可能為字元,也可能為空(迴文長度為偶數的時候),這樣對於長度為n的字串,總共就有2n-1個地方需要去列舉 int left = i/2,right = i/2; if (i % 2) right++; //可以在紙上畫一畫就明白了 while(s[left] == s[right]){ ans++; left--; right++; if (left < 0 || right >= n) break; } } return ans; } };
方法二(2):受方法一的啟發,我們定好一個右端點j後,不斷在前面選左端點i,如果s[i] != s[j],那肯定不行,否則我們就要判斷s[i+1] ~ s[j-1]是不是迴文串。我們突然靈光一現,好像一個記憶化。我們開一個二維陣列dp[i] [j]表示從下標i到下標j 這樣的字串是否是迴文串,轉移方程式為:
dp[i][j] = dp[i +1][j - 1] && (s[i] == s[j])
這樣的時間複雜度也是O(n^2),但與方法二(1)相比還多了個數組,所以總體沒有方法二(1)優的
class Solution { public: int countSubstrings(string s) { int n = s.size(); vector<vector<bool>> dp(n,vector<bool>(n,false)); int ans = 0; for (int i = 0; i < n; i++) dp[i][i] = true,ans++; for (int i = 0; i < n; i++){ for (int j = 0; j < i; j++){ if (s[i] != s[j]) continue; else{ if (dp[j+1][i-1] || j+1 > i-1){ ans++; dp[j][i] = true; } } } } return ans; } };
方法三:馬拉車演算法,沒有接觸過的可以去看練習題目的官方題解,這裡記錄一些要點:
1.馬拉車演算法要求我們維護當前最大的迴文的右端點 rmax以及這個迴文右端點對應的迴文中心mid。
2.在拓展後的字串左邊加‘$’,右邊加‘!’的目的是:為了省事,在拓展的時候防止下標越界!
3.對於每一個f[i] 來說(f[i]就是以i為迴文中心的最大回文半徑),如果i在rmax裡面,那麼我們就可以利用先前已經知道答案:f[i] = min(rMax - i + 1, f[2 * iMax - i]),否則先前知道的答案對i沒什麼用:f[i] = 1.
4.對於拓展後的字串對應的f[i] 來說,它所對應的原迴文串的長度是 f[i] - 1(可以用數學關係推)
時間複雜度為O(n),可以說是很快了
class Solution {
public:
int countSubstrings(string s) {
int n = s.size();
string S = "$";
for (int i = 0; i < n; i++){
S.push_back('#');
S.push_back(s[i]);
}
S += "#";
S += "!";
vector<int> f(S.size());
int ans = 0,mid = 0,rmax = 0;
for (int i = 1; i < S.size(); i++){
if (i <= rmax){
f[i] = min(f[2*mid-i], rmax-i+1);
}else f[i] = 1;
while(S[i-f[i]] == S[i+f[i]]) f[i]++;
if (i+f[i]-1 > rmax){
rmax = i+f[i]-1;
mid = i;
}
ans += f[i]/2;
}
return ans;
}
};