(待填坑)【字符串】manacher(马拉车)算法
生活随笔
收集整理的這篇文章主要介紹了
(待填坑)【字符串】manacher(马拉车)算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
知識點
manacher算法可以在時間復雜度為O(n)的情況下,求出一個字符串的最長回文子串的算法。
一 . 實現步驟
① 在字符串的各個字母中間加上一個字符,使其變成奇數字符串。對于原來是偶數字符串來說,添加的字符“#”作為對稱中心;原來是奇數字符串的最中間的字母為對稱中心。
?② 設字符串的對稱中心為mid,r表示最長回文串的右端點在哪里,i 表示當前掃描到的節點,j 節點表示 i 節點關于字符串對稱中心對稱的位置。
模板題
洛谷 P3805 【模板】manacher 算法
題目描述
給出一個只由小寫英文字符?組成的字符串?S?,求?S?中最長回文串的長度 。
字符串長度為?n。
輸入格式
一行小寫英文字符??組成的字符串?S。
輸出格式
一個整數表示答案。
輸入輸出樣例
輸入 #1
aaa輸出 #1
3說明/提示
。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #define ll long long using namespace std; const int maxn=1e8+5; char c[maxn<<1],s[maxn]; int p[maxn<<1]; int r,mid,ans=1; int main() {scanf("%s",s);int len=strlen(s);c[0]=c[1]='#';p[1]=1;for(int i=1;i<=len;++i){c[2*i]=s[i-1];c[2*i+1]='#';}c[(len<<1)+2]='$';//在各個字母之間加上字符進行分割for(int i=0;i<(len<<1)+2;++i){if(i<r) p[i]=min(p[(mid<<1)-i],p[mid]+mid-i);else p[i]=1;while(c[i-p[i]]==c[i+p[i]]) p[i]++;if(p[i]+i>r){r=p[i]+i;mid=i;}}for(int i=0;i<=(len<<1)+2;++i){ans=max(ans,p[i]-1); }printf("%d",ans); return 0; }相關練習
1 .?洛谷 P1659 [國家集訓隊]拉拉隊排練
思路:由題得“連續的一段女生,有奇數個,并且他們手中的牌子所寫的字母,從左到右和從右到左讀起來一樣,那么就被稱作和諧小群體” ,說明需要找的這個字符串是回文字符串,可以使用manacher算法。又因為找尋的回文字符串的字符個數為奇數個,其實只需要在字符串首和尾部加上其他符號進行特判即可。需要注意數據范圍很大,需要使用快速冪進行加速運算。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define re register using namespace std; ll int n,k; const int maxn=1e7+5,mod=19930726; char c[maxn],s[maxn<<1]; ll int p[maxn<<1],front[maxn]; ll int mid,r,len; inline ll int quick_pow(ll int x,ll int y) //快速冪加速 {ll int ans=1;while(y){if(y&1){ans=(ans*x)%mod;}x=(x*x)%mod;y>>=1;}return ans; }int main() {scanf("%d%d",&n,&k);scanf("%s",c+1);len=n;s[0]=s[1]='#';for(re ll int i=1;i<=len;++i){s[i<<1]=c[i];s[i<<1|1]='#';}len=(len<<1)+2;s[len]='$';for(re ll int i=1;i<len;++i){if(i<r) p[i]=min(p[(mid<<1)-i],r-i);else p[i]=1;while(s[i-p[i]]==s[p[i]+i]) p[i]++; //這里要注意是i-p[i]!!!if(p[i]+i>r){r=p[i]+i;mid=i;}if((p[i]-1)&1) front[p[i]-1]++; //用桶標記有幾個奇數字符串}ll int sum=0,res=1;if(n%2==0) n--; //只需統計奇數的字符串for(re ll int i=n;i>=1;i-=2){sum+=front[i]; //統計有幾個和諧小群體if(sum<=k){res=(res*quick_pow(i,sum))%mod; //記錄前k個和諧小群體的女生個數的乘積k-=sum; }else{res=(res*quick_pow(i,k))%mod; break;}}if(sum<k) res=-1; //最后的總數也不能超過k,標記為-1printf("%lld",res);return 0; }2 .?洛谷 P4555 [國家集訓隊]最長雙回文串
思路:(待填坑)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e6+5; char c[maxn<<1],s[maxn<<1]; int p[maxn<<1],len=0,r,mid,len1[maxn<<1],len2[maxn<<1]; //len1[i]表示以i開頭的最長回文串的長度 //len2[i]表示以i結尾的最長回文串的長度 int main() {scanf("%s",c+1);len=strlen(c+1);s[0]=s[1]='#';for(int i=1;i<=len;++i){s[i<<1]=c[i];s[i<<1|1]='#';}len=(len<<1)+2;s[len]='$';for(int i=1;i<len;++i){if(i<r) p[i]=min(p[(mid<<1)-i],p[mid]+mid-i);else p[i]=1;while(s[i-p[i]] == s[i+p[i]]) p[i]++;if(p[i]+i>r) {mid=i;r=p[i]+i;}len1[i-p[i]+1]=max(len1[i-p[i]+1],p[i]-1); //求出以i開頭的飽和回文串的最大長度len2[i+p[i]-1]=max(len2[i+p[i]-1],p[i]-1); //求出以i結尾的飽和回文串的最大長度}//求出以i開頭的飽和與不飽和回文串的最大長度for(int i=3;i<len;i+=2){len1[i]=max(len1[i],len1[i-2]-2); }//求出以i開頭的飽和與不飽和回文串的最大長度for(int i=len-1;i>=3;i-=2){len2[i]=max(len2[i],len2[i+2]-2);}int Max=0;for(int i=3;i<=len;i+=2){if(len1[i]&&len2[i]) //在同一節點能保證兩個子串都是回文串且總長度最長Max=max(Max,len1[i]+len2[i]);}printf("%d",Max);return 0; }總結
以上是生活随笔為你收集整理的(待填坑)【字符串】manacher(马拉车)算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028全球便携式网络测试仪行
- 下一篇: 通过微透镜阵列的传播