字符串-Manacher算法(你知道马拉车算法吗?)
文章目錄
- 原理
- 奇偶問題
- p[]數組
- 馬拉車求p[]
- 模板
- 例題
- P3805 【模板】manacher算法
- P1659 拉拉隊排練
原理
馬拉車算法當然不是馬拉著車的奇奇怪怪的東西,是Manacher’s Algorithm的音譯。
Manacher’s Algorithm馬拉車算法是一種可以在O(n)O(n)O(n)線性時間內求最長回文子串的算法(Manacher是人名,發明者)。所謂的回文也就是正讀反讀都是一樣的,比如noonnoonnoon、levellevellevel,那在一個字符串中找最長的回文子串,一般使用中心擴展法,也就是枚舉每一個字符為對稱中心,然后向左右延伸并判斷是否相等,但這種方法的時間復雜度是O(n2)O(n^2)O(n2)。
分析中心擴展法效率差的原因,是因為它做了大量重復計算,比如字符串aaaaaaaaaaaaaaaaaaaaaaaaaaa、abababaabababaabababa,也就是最壞的情況——各個回文子串相互重疊。而馬拉車算法則通過一些空間記錄和回文對稱的特點來避免這種重復計算。
奇偶問題
首先需要解決字符串長度奇偶時,對稱中心不一致的問題。如串abaabaaba和abbaabbaabba都是回文串,但是它們的對稱中心分別是不一致的,前者是bbb,后者是位于bbb中間。
我們在每個字符前后加上一個’#‘符,并且為了后續不越界和求原下標方便,我們讓新字符串ttt的下標從1開始,即下標0處也賦’#’。這樣處理后字符串的長度就都是奇數了,對稱中心自然也都是二分之一處的字符,不會再是位于兩字符中間的情況。
p[]數組
現在我們引用一個數組p[]來表示中心擴展的最大長度,而它就是去掉’#'后原回文子串的長度,也就是我們求的答案。
以字符串ababaabababaabababaab為例,通過PPP的下標iii減去P[i]P[i]P[i]再除以222就能求出原回文子串,如下標888處P[8]=3P[8]=3P[8]=3,(8?3)/2=2(8-3)/2=2(8?3)/2=2,即在原串SSS中下標222開始長度為333的子串是一個回文子串(abaabaaba)。
馬拉車求p[]
前面都是準備工作,下面才是馬拉車的核心算法,它充分利用了回文的對稱性,比如說對于回文串ababaababaababa,不難發現它的p[]p[]p[]數組也是關于中心對稱的,如下圖:
但這只限于回文串中可以這樣直接鏡像,還需要推導至一般情況,仍以串ababaabababaabababaab為例。
設midmidmid為對稱中心,rrr表示回文串的右邊半徑(r=mid+p[i]r=mid+p[i]r=mid+p[i]),下標iii即當前要求的p[i]p[i]p[i],下標jjj表示iii關于中心midmidmid鏡像對稱的下標,分如下三種情況:
合法范圍內
就是上面討論的情形,直接賦值即可。
jjj遇到原字符串左邊界
此時不能直接賦值,因為jjj位置處是因為向左到盡頭越界,而鏡像處的iii向左沒有越界,應該繼續比較,即用中心擴展法。
雖然此處答案都是1,但jjj是因為到了首字符‘#’必不相等,而iii是可以匹配的,只是此處aaa和bbb匹配失敗。
iii>=rrr
同上面一樣不能利用對稱性了,將p[i]p[i]p[i]置0,用中心擴展法。
同時注意更新midmidmid和rrr,也就是當更新p[i]p[i]p[i]后,求出新的r(p[i]+i)r(p[i]+i)r(p[i]+i),若大于舊的rrr,就更新midmidmid和rrr,即設置當前點iii為新的對稱中心。
(插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/
模板
#include <iostream> #include <string> using namespace std; const int maxn = 1000006; int p[maxn]; string s, t; int main() {cin >> s;int n = s.size();t = "#";for (int i = 0; i < n; i++) {t += '#';t += s[i];}t += '#';int m = t.size();int mid = 0, r = 0;int len = 0, st = 0;//回文串長度和起始位置for (int i = 1; i < m - 1; i++) {int j = mid * 2 - i;if (i < r) //右邊半徑內p[i] = min(r - i, p[j]);//防止超出relse p[i] = 0; //>=r情況while (t[i + 1 + p[i]] == t[i - 1 - p[i]])p[i]++; //中心擴展if (i + p[i] > r) { //更新rmid = i;r = i + p[i];}if (p[i] > len) { //順便更新答案len = p[i];st = (i - p[i]) / 2;}}cout << len << "\n" << s.substr(st, len);return 0; }注意時間復雜度是O(n)O(n)O(n),當里面的while循環中心擴展后,下次循環是不會再訪問到這些節點的,會直接用對稱性。
例題
P3805 【模板】manacher算法
洛谷P3805 【模板】manacher算法
題目描述
給出一個只由小寫英文字符 \texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,…y,z 組成的字符串 SS ,求 SS 中最長回文串的長度 。
字符串長度為 nn。
輸入格式
一行小寫英文字符 \texttt a,\texttt b,\texttt c,\ldots\texttt y,\texttt za,b,c,…y,z 組成的字符串 SS。
輸出格式
一個整數表示答案。
輸入輸出樣例
輸入 #1
aaa
輸出 #1
3
說明/提示
1≤n≤1.1×107
P1659 拉拉隊排練
洛谷P1659 [國家集訓隊]拉拉隊排練
題目描述
艾利斯頓商學院籃球隊要參加一年一度的市籃球比賽了。拉拉隊是籃球比賽的一個看點,好的拉拉隊往往能幫助球隊增加士氣,贏得最終的比賽。所以作為拉拉隊隊長的楚雨蕁同學知道,幫助籃球隊訓練好拉拉隊有多么的重要。
拉拉隊的選拔工作已經結束,在雨蕁和校長的挑選下,n位集優秀的身材、舞技于一體的美女從眾多報名的女生中脫穎而出。這些女生將隨著籃球隊的小伙子們一起,和對手抗衡,為艾利斯頓籃球隊加油助威。
一個陽光明媚的早晨,雨蕁帶領拉拉隊的隊員們開始了排練。n個女生從左到右排成一行,每個人手中都舉了一個寫有26個小寫字母中的某一個的牌子,在比賽的時候揮舞,為小伙子們吶喊、加油。
雨蕁發現,如果連續的一段女生,有奇數個,并且他們手中的牌子所寫的字母,從左到右和從右到左讀起來一樣,那么這一段女生就被稱作和諧小群體。
現在雨蕁想找出所有和諧小群體,并且按照女生的個數降序排序之后,前K個和諧小群體的女生個數的乘積是多少。由于答案可能很大,雨蕁只要你告訴她,答案除以19930726的余數是多少就行了。
輸入格式
輸入為標準輸入。
第一行為兩個正整數n和K,代表的東西在題目描述中已經敘述。
接下來一行為n個字符,代表從左到右女生拿的牌子上寫的字母。
輸出格式
輸出為標準輸出。
輸出一個整數,代表題目描述中所寫的乘積除以19930726的余數,如果總的和諧小群體個數小于K,輸出一個整數-1。
輸入輸出樣例
輸入 #1
5 3
ababa
輸出 #1
45
說明/提示
樣例說明
和諧小群體女生所拿牌子上寫的字母從左到右按照女生個數降序排序后為ababa, aba, aba, bab, a, a, a, b, b,前三個長度的乘積為 。
跑一遍馬拉車,并使用cnt[]數組記錄回文串長度對應數量,過濾奇數的,從大往小循環即可,另外注意直接乘會超時,用快速冪。
#include <iostream> #include <string> using namespace std; const int maxn = 2000006; const int mod = 19930726; typedef long long ll; int p[maxn], cnt[maxn]; string s, t; ll n, k; ll qpow(ll x, ll y) {ll res = 1;while (y) {if (y & 1)res = (res * x) % mod;x = (x * x) % mod;y >>= 1;}return res; } int main() {cin >> n >> k >> s;t = "#";for (int i = 0; i < n; i++) {t += '#';t += s[i];}t += '#';ll m = t.size();int mid = 0, r = 0;for (int i = 1; i < m - 1; i++) {int j = mid * 2 - i;if (i < r)p[i] = min(r - i, p[j]);else p[i] = 0; while (t[i + 1 + p[i]] == t[i - 1 - p[i]])p[i]++; if (i + p[i] > r) { mid = i;r = i + p[i];}if (p[i] % 2)cnt[p[i]]++;}ll sum = 0, ans = 1;for (int i = n; i >= 1; i--) {if (i % 2 == 0)continue;sum += cnt[i];ll cur = min(sum, k);k -= sum;ans = (ans * qpow(i, cur)) % mod;if (k < 0)break;}if (k > 0)ans = -1;cout << ans;return 0; }原創不易,請勿轉載(本不富裕的訪問量雪上加霜 )
博主首頁:https://wzlodq.blog.csdn.net/
來都來了,不評論兩句嗎👀
如果文章對你有幫助,記得一鍵三連?
總結
以上是生活随笔為你收集整理的字符串-Manacher算法(你知道马拉车算法吗?)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 3507 斜率优化入学习
- 下一篇: Modbus 简单认识(楼宇自动化系统背