日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P4445 最长回文串

發布時間:2023/12/3 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P4445 最长回文串 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

P4445 最長回文串

題目描述

順序和逆序讀起來完全一樣的串叫做回文串。比如acbcaacbcaacbca是回文串,而abcabcabc不是(abc的順序為abcabcabc,逆序為cbacbacba,不相同)。

輸入長度為nnn的串SSS,求SSS的最長雙回文子串TTT,即可將TTT分為兩部分XXXYYY(∣X∣,∣Y∣≥1∣X∣,∣Y∣≥1)(|X|,|Y|≥1∣X∣,∣Y∣≥1)X,Y1X,Y1XXXYYY都是回文串。

題目解答

將串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 最长回文串的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。