首先,強烈安利一篇文章,這篇文章對於\(manacher\)的講解本人感覺非常到位。
傳送門相信大家都知道的乙個方法\(:\)列舉字串的每乙個位置作為回文子串的對稱中心,同時向左向右擴充套件,判斷是否相等,然後每次儲存之前求取的最大回文子串長度,時間複雜度為\(o(n^2)\)。
在列舉時,還需要考慮對奇數回文串和偶數回文串分開處理,因為奇數回文串的對稱中心是單個字元,而偶數回文串的對稱中心為中間兩字元的中間位置。為了結決這個問題,我們可以在每個字元的前後分別插入乙個無關的字元\((\)具體是什麼對結果無影響\()\),這樣無論是奇數串還是偶數串都會轉變為奇數串。
首先,介紹幾個\(manacher\)演算法中的幾個概念\(:\)
然後,我們來說一下\(manacher\)的演算法流程\(:\)
第一種情況,當我們當前要擴充套件的點在最右回文邊界\(max\_r\)的右側時,由於其右側點的情況我們還是未知的,所以我們只能採取樸素的做法,以要擴充套件的點為對稱中心向兩邊擴充套件,同時更新\(radius\)陣列,最右回文邊界和最右回文邊界的對稱中心。
第二種情況,當我們要擴充套件的點在最右回文邊界的左側時,又要分三種情況來考慮\(:\)
首先說明一下下面將出現的變數\(:\)
\(max\_l\)表示最右回文邊界關於對稱中心的對稱點,\(p\)為當前要擴充套件的點,\(p_1\) 為當前要擴充套件的點關於對稱中心的對稱點,\(p_2\) 為以 \(p_1\) 為對稱中心的回文子串的左邊界。
很顯然,這種情況下,\(radius[p]=radius[p_1]\)。
這種情況下,若我們仍取\(radius[p_1]\),很顯然就會涉及到\(max\_r\)之外我們未知的區域,所以\(radius[p]\)只能取\(max\_r-p\)。
這種情況下,我們就需要繼續向兩邊擴充套件\(p\),但顯然我們只需要從\(max\_r\)向右擴充套件即可。
那麼,\(manacher\)的演算法流程大致就是這樣了,下面我們來感性證明一下時間複雜度\(:\)
上面的兩種情況中,第二種的\(1,2\)的時間複雜度都是\(o(1)\)的,第一種情況和第二種的\(3\),\(max\_r\)都是不斷向右擴充套件,沒有回頭的情況,在判斷回文半徑時也沒有對\(max\_r\)內的點進行判斷,所以\(max\_r\)是從字串的左端點向右擴充套件到右端點,總複雜度為\(o(n)\)。
具體細節說清楚了,**應該就很簡單了。
code:
#include #include #include #define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using std::string;
const int maxn = 11000002;
string x, s;
int radius[maxn << 1], max_right, mid, ans;
int main()
printf("%d\n", ans);
return 0;
}
完結撒花(✪ω✪) Manacher演算法學習筆記
manacher演算法是乙個求乙個字串中最長回文連續子串行的演算法 p3805 模板 manacher 演算法 description 求最長回文子串的長度 solution 我們先引入乙個 o n 2 的做法,列舉每個字元為回文串的中心,嘗試向兩邊擴充套件,用擴充套件的最大長度更新答案。為了下文描...
演算法學習 manacher
沒有前置知識 解決的問題 大多數都和回文串有關 例如 字串中長度最長的回文串 演算法學習 請牢記這個目的 先來講樸素演算法 從1 n,對每個字元都從其自身開始,向兩邊遞推,如果左右兩邊字元相同,範圍 1,長度 2 這樣的複雜度是n 2的 而 manacher 的優化方式和 kmp 有所類似,都是利用...
Manacher演算法 學習總結
求乙個序列的最大回文子串,我們需要需要用到manacher演算法。其作用在於能夠在o n 的時間內求出最長回文子串,同時也能夠求出回文子串的個數。且時間效率高,十分簡潔。我們知道,回文子串分為奇數回文子串和偶數回文子串。由於兩種情況的處理較為麻煩,我們可以考慮在期間插入字元在簡化問題。例如例子 ab...