P4445 最长回文串
P4445 最長回文串
題目描述
順序和逆序讀起來完全一樣的串叫做回文串。比如acbcaacbcaacbca是回文串,而abcabcabc不是(abc的順序為abcabcabc,逆序為cbacbacba,不相同)。
輸入長度為nnn的串SSS,求SSS的最長雙回文子串TTT,即可將TTT分為兩部分XXX,YYY,(∣X∣,∣Y∣≥1∣X∣,∣Y∣≥1)(|X|,|Y|≥1∣X∣,∣Y∣≥1)(∣X∣,∣Y∣≥1∣X∣,∣Y∣≥1)且XXX和YYY都是回文串。
題目解答
將串sss進行預處理增加’$‘和’#'字符得到nsnsns串以便使用Manacher算法.
使用Manacher算法求以每個位置為中心的最長回文串長度數組npnpnp.
算法一.
根據數組ppp,處理出數組lft,rgtlft,rgtlft,rgt
lft[i]lft[i]lft[i]表示nsnsns中以iii為右端點的最長回文串的中心位置.
rgt[i]rgt[i]rgt[i]表示nsnsns中以iii為左端點的最長回文串的之心位置.
ps:由于nsnsns字符串中包含了#\##字符,因此回文串中心到一端的距離,就可以實際表示一個回文串的長度.
由于nsnsns串特殊的奇偶性,我們枚舉iii,計算rgt[i+1]?lft[i]{rgt[i+1]-lft[i]}rgt[i+1]?lft[i]的最大值就是答案.
問題變成了如何求lft,rgtlft,rgtlft,rgt數組.
以lftlftlft為例,rgtrgtrgt求法類似:
枚舉回文串中心的位置iii,則lft[i,i+np[i])lft[i,i+np[i])lft[i,i+np[i])區間內未設置值得位置全都設置成為iii.
實現代碼
#include <iostream> #include <cstdio> #include <queue> #include <vector> #include <cstring>const int N = 100007; char s[N],ns[N<<1];int np[N<<1];#define pr(x) std::cout << #x << ":" << x << std::endlstd::vector<int> vec[N<<1]; int vis[N<<1];int lft[N<<1],rgt[N<<1];int Manacher() {int len = 0,mx = 0,id;ns[len++] = '$';ns[len++] = '#';for(int i = 0;s[i];++i)ns[len++] = s[i],ns[len++] = '#';for(int i = 1;i < len;++i) {np[i] = mx > i ? std::min(np[2*id-i],mx-i):1;while(ns[i-np[i]] == ns[i+np[i]]) ++np[i];if(np[i]+i > mx) mx = np[i]+i,id = i;}int p = 0;for(int i = 0;i < len;++i) {for(;p < i + np[i];++p) {lft[p] = i;}} p = len;for(int i = len-1;i > 0;--i) {for(;p > i - np[i];--p) {rgt[p] = i;}}int ans = 0;for(int i = 1;i < len;i++) {ans = std::max(ans,rgt[i+1] - lft[i]);} return ans; }int main() {std::cin >> s;std::cout << Manacher() << std::endl;return 0; }算法二.
計算數組lft,rgtlft,rgtlft,rgt
lft[i]lft[i]lft[i]表示以iii為右端點的最長回文子串在sss中的實際長度.
rgt[i]rgt[i]rgt[i]表示以iii為左端點的最長回文子串在sss中的實際長度.
那么答案就是枚舉iii為偶數,ans=max(lft[i]+rgt[i+2])ans = max(lft[i] + rgt[i+2])ans=max(lft[i]+rgt[i+2])
以lftlftlft為例,rgtrgtrgt類似:
從小到大枚舉iii,并用一個優先隊列維護當前最小的回文串中心點iii,設定在i+np[i]i+np[i]i+np[i]位置將優先隊列中的iii設為失效.
每次從優先隊列中取出的第一個有效的數即是lft[i]lft[i]lft[i].
這種算法的思路與算法一本質上是一樣的,只不過枚舉量不同,算法一枚舉的是lft[i]lft[i]lft[i],然后利用單調性將時間復雜度降低到了O(n)O(n)O(n)
算法二枚舉的是iii,需要用數據結構來維護,時間復雜度是O(nlogn)O(nlogn)O(nlogn)
#include <iostream> #include <cstdio> #include <queue> #include <vector> #include <cstring>const int N = 100007; char s[N],ns[N<<1];int np[N<<1];#define pr(x) std::cout << #x << ":" << x << std::endlstd::vector<int> vec[N<<1]; int vis[N<<1];int lft[N<<1],rgt[N<<1];int Manacher() {int len = 0,mx = 0,id;ns[len++] = '$';ns[len++] = '#';for(int i = 0;s[i];++i)ns[len++] = s[i],ns[len++] = '#';for(int i = 1;i < len;++i) {np[i] = mx > i ? std::min(np[2*id-i],mx-i):1;while(ns[i-np[i]] == ns[i+np[i]]) ++np[i];if(np[i]+i > mx) mx = np[i]+i,id = i;}for(int i = 0;i < (N << 1);++i) vec[i].clear();memset(vis,0,sizeof(vis)); std::priority_queue<int,std::vector<int>,std::greater<int> > lQ;for(int i = 1;i < len;++i) {for(auto u : vec[i]) vis[u] = 1;while(!lQ.empty() && vis[lQ.top()]) lQ.pop();lft[i] = 1;if(lQ.empty()) {lQ.push(i);vec[i + np[i]].push_back(i);continue;}else {lft[i] = std::max(lft[i],(i-lQ.top()+1)/2*2+(lQ.top()%2==0));}lQ.push(i);vec[i + np[i]].push_back(i);}for(int i = 0;i < (N << 1);++i) vec[i].clear();memset(vis,0,sizeof(vis)); std::priority_queue<int> gQ;for(int i = len-1;i;--i) {for(auto u : vec[i]) vis[u] = 1;while(!gQ.empty() && vis[gQ.top()]) gQ.pop();rgt[i] = 1;if(gQ.empty()) {gQ.push(i);vec[i - np[i]].push_back(i);continue;}else {rgt[i] = std::max(rgt[i],(gQ.top()-i+1)/2*2+(gQ.top()%2==0));}gQ.push(i);vec[i - np[i]].push_back(i);}int ans = 0;for(int i = 1;i+2 <= len;++i) if(i%2==0)ans = std::max(ans,lft[i]+rgt[i+2]);return ans; }int main() {std::cin >> s;std::cout << Manacher() << std::endl;return 0; }總結
以上是生活随笔為你收集整理的P4445 最长回文串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线段树-楼房重建-洛谷-P4198
- 下一篇: 牛客练习赛29 题解