演算法練習week9--leetcode647
阿新 • • 發佈:2018-11-02
問題連結
題目解析
計算字串的迴文子串數。
解題思路
一個小問題,子串(Substring)、子陣列(Subarray)和子序列(Subsequence)的區別:子串和子陣列是等同的,特點是連續的,比如[1,2,3]的子串有(1), (2), (3), (1,2), (2,3), (1,2,3)。而子序列不一定相鄰,但相對順序一致,比如(1,3)是[1,2,3]的一個子序列。
方法有很多種,簡單講一些。
方法一:DP
一開始定義DP[i][j]為i、j之間的迴文子串數,很是麻煩,還需要另外的陣列記錄子串[i, j]是否是迴文的。其實沒有必要,直接將DP[i][j]定義成子串[i, j]是否是迴文串。外迴圈 ii從 n−1n−1 往 00 遍歷,內迴圈 jj 從 ii 往 n−1n−1 遍歷,若s[i]==s[j]:
- 若i==j,則dp[i][j]=true;
- 若i和j是相鄰的,則dp[i][j]=true;
- 若i和j中間只有一個字元,則dp[i][j]=true;
- 否則,檢查dp[i+1][j-1]是否為true,若為true,那麼dp[i][j]就是true。
前三條可以合併,即 j−i≤2j−i≤2。求得dp[i][j]真值後,如果其為true,最終結果res++。
時間複雜度:O(n2)O(n2)。
方法一參考程式碼:
class Solution { public: int countSubstrings(string s) { int len = s.size(), res = 0; vector<vector<bool>> dp(len, vector<bool>(len, false)); for (int i = len - 1; i >= 0; --i) { for (int j = i; j < len; ++j) { dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1]); if (dp[i][j]) ++res; } } return res; } };
方法二:迴文中心法
本題可以不用DP,而是採用一種巧妙的方法:迴文中心法。什麼意思呢?考慮不同的迴文中心,往兩邊擴散,求得迴文數。需要考慮兩種情況:如果是奇數長度迴文串,了麼迴文中心為最中間的一個字元;如果是偶數長度迴文串,這回文中心為最中間的兩個字元。
每個迴文子串只有一個迴文中心,所以這種方法不會重複計算,也不會漏算。
時間複雜度:O(n2)O(n2)。
方法二參考程式碼:
class Solution {
public:
int countSubstrings(string s) {
int len = s.size(), res = 0;
for (int i = 0; i < len; ++i) {
int mid1 = i, mid2 = i;//奇數
while (mid1 >= 0 && mid2 < len && s[mid1] == s[mid2]) {
--mid1; ++mid2; ++res;
}
mid1 = i, mid2 = i+1;//偶數
while (mid1 >= 0 && mid2 < len && s[mid1] == s[mid2]) {
--mid1; ++mid2; ++res;
}
}
return res;
}
};
方法三:“馬拉車”演算法
神奇的演算法,先馬一下,學會再寫上。聽說時間複雜度是 O(n)O(n)。
好了,學到了,請參考:什麼是馬拉車演算法?
利用馬拉車演算法,可以得到所有情況下的最大半徑,以s[i]為中心,RL[i]為半徑的迴文串中含有的字迴文串數目是 RL[i]/2RL[i]/2 個。
方法三參考程式碼:
class Solution {
public:
int countSubstrings(string s) {
//預處理
string t = "#";
for (int i = 0; i < s.size(); ++i) {
t += s[i];
t += "#";
}
vector<int> RL(t.size(), 0);
int MaxRight = 0, pos = 0;
int res = 0;
for (int i = 0; i < t.size(); ++i) {
RL[i] = MaxRight > i ? min(RL[2 * pos - i], MaxRight - i) : 1;
while (i-RL[i] >=0 && i+RL[i] < t.size() && t[i + RL[i]] == t[i - RL[i]])//擴充套件,注意邊界
++RL[i];
//更新最右端及其中心
if (MaxRight < i + RL[i] -1) {
MaxRight = i + RL[i] -1;
pos = i;
}
res += RL[i]/2;
}
return res;
}
};