Manacher基础学习
马拉车的模板
void init(){ int k=0; ma[k++] ='$'; for(int i=0;i<n;i++){//n是s的长度 ma[k++]='#'; ma[k++]=s[i]; } ma[k++]='#'; len=k; } int Manacher(){ Len[0]=0; int sum = 0,id=0,mx = 0; for(int i=1;i<len;i++){ if(i < mx) Len[i] = min(mx - i, Len[2 * id - i]); else Len[i] = 1; while(i-Len[i]>0 && i+Len[i]<len && ma[i - Len[i]]== ma[i + Len[i]]){ Len[i]++; } if(Len[i] + i > mx){ mx = Len[i] + i; id = i; sum=max(Len[i],sum); } } return (sum - 1); }
ma[] $ # a # b # a # a #
Len[] 1 2 1 4 1 2 3 2 1
复杂度o(n)
模板题1hdu3068,问最长的回文字符长度
模板题2 hdu3294,问这个字符在字符串中最长回文子串,然后就好了
因为两题真就模板题,就只放3068的了
#include <bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() const int maxn=1e6+10; int t; char s[maxn]; char ma[maxn*2]; int Len[maxn*2],len,pos; void init(int l){ int k=0; ma[k++] = '$'; for(int i=0;i<l;i++){ ma[k++]='#'; ma[k++]=s[i]; } ma[k++]='#'; len=k; } int Manacher(){ Len[0]=0; int sum = 0,id=0,mx = 0; for(int i=1;i<len;i++){ if(i < mx) Len[i] = min(mx - i, Len[2 * id - i]); else Len[i] = 1; while(i-Len[i]>0 && i+Len[i]<len && ma[i - Len[i]]== ma[i + Len[i]]) Len[i]++; if(Len[i] + i > mx){ mx = Len[i] + i; id = i; sum=max(Len[i],sum); } } return (sum - 1); } int main() { while(~scanf("%s",s)){ init(strlen(s)); printf("%d\n",Manacher()); } return 0; }
模板题3 hdu4513 ,问一排身高的人,有多少是凸起回文身高
比如2 3 2算3 但2 1 2算1
其实就是里面将ma[i-Len[i]+2]>=ma[i-Len[i]]的条件。
#include <bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() const int maxn=1e5+10; int t,n; int s[maxn]; int ma[maxn*2]; int Len[maxn*2],len,pos; void init(){ int k=0; ma[k++] = -2; for(int i=0;i<n;i++){ ma[k++]=-1; ma[k++]=s[i]; } ma[k++]=-1; len=k; } int Manacher(){ Len[0]=0; int sum = 0,id=0,mx = 0; for(int i=1;i<len;i++){ if(i < mx) Len[i] = min(mx - i, Len[2 * id - i]); else Len[i] = 1; while(i-Len[i]>0 && i+Len[i]<len && ma[i - Len[i]]== ma[i + Len[i]] && ma[i-Len[i]+2]>=ma[i-Len[i]]){ Len[i]++; } if(Len[i] + i > mx){ mx = Len[i] + i; id = i; sum=max(Len[i],sum); } } return (sum - 1); } int main() { scanf("%d",&t); while(t--){ scanf("%d",&n); for(it i=0;i<n;i++){ scanf("%d",&s[i]); } init(); printf("%d\n",Manacher()); } return 0; }
题意:
给t个字符串s,题意好难说啊,
样例输入 输出
5
a a
abcdfdcecba abcdfdcba
abbaxyzyx xyzyx
codeforces c或者s
accbbca acbbca
就是求s的前缀,和s的后缀组成最长的回文子串,输出它
样例二s的前缀abcdfd和s的后缀cba可以组成abcdfdcba
思路:
如果头和尾是相等,往里找,头和尾不相等的,比如第五个样例accbbca,ac是相等的所以只要cbb里找两头最长的回文字符串,直接马拉车
找位置的时候需要条件要符合i-Len[i]==0 or i+Len[i]==len(模拟赛的时候这个条件没看到直接裂开了,而且本来的马拉车模板是错的)
#include <bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() const int maxn=1e6+10; int t; char s[maxn],ss[maxn]; char ma[maxn*2]; int Len[maxn*2],cc,pos; void init(int l,int r){ int k=0; ma[k++] = '$'; for(int i=l;i<=r;i++){ ma[k++]='#'; ma[k++]=s[i]; } ma[k++]='#'; cc=k; } int Manacher(){ Len[0]=0; int sum = 0,id=0,mx = 0; pos=0; for(int i=1;i<cc;i++){ if(i < mx) Len[i] = min(mx - i, Len[2 * id - i]); else Len[i] = 1; while(i-Len[i]>0 && i+Len[i]<cc && ma[i - Len[i]]== ma[i + Len[i]]) Len[i]++; if(Len[i] + i > mx){ mx = Len[i] + i; id = i; if(Len[i]>sum &&( i-Len[i]==0 || i+Len[i]==cc)){ sum=Len[i];pos=i; } } } return (sum - 1); } int main() { scanf("%d",&t); while(t--){ scanf("%s",s); int ls=strlen(s); int l=0,r=ls-1; while(s[l]==s[r] && l<=r){ ss[l]=s[l];l++;r--; } if(l>r){ printf("%s\n",s);continue; } init(l,r); int chang=Manacher(); for(it i=0;i<l;i++){ printf("%c",ss[i]); } for(it i=pos-chang+1;i<=pos+chang-1;i+=2){ printf("%c",ma[i]); } for(it i=l-1;i>=0;i--){ printf("%c",ss[i]); } printf("\n"); } return 0; }
求回文子串的个数
int Manacher(){ Len[0]=0; int sum = 0,id=0,mx = 0,cnt=0; pos=0; for(int i=1;i<cc;i++){ if(i < mx) Len[i] = min(mx - i, Len[2 * id - i]); else Len[i] = 1; while(i-Len[i]>0 && i+Len[i]<cc && ma[i - Len[i]]== ma[i + Len[i]]) Len[i]++; if(Len[i] + i > mx){ mx = Len[i] + i; id = i; } cnt+=Len[i]/2; } return cnt;//cnt回文子串的个数 }
比如abaadada
有a b a a d a d a aba aa ada ada adada dad 一共14个,cnt的输出也是14
题意:
问给出的字符串能不能分割成三个回文字符串,能输出yes,不能输出no
思路:
因为Len[i]给的是这个字符回文半径,所以只要找i-Len[i]==0 or i+Len[i]==len,等于0的的右边界 l ,和和等于len的左边界 r ,的中间值是否大于l-r+1<Len[mid]*2,同时满足l<r
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define ll long long #define ull unsigned long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define pii pair<int,int> #define mak(n,m) make_pair(n,m) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 998244353 #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() const int maxn=2e4+10; int n; char s[maxn],ma[maxn*2]; int Len[maxn*2],len; int pre1[maxn*2],pre2[maxn*2]; void init(int l){ int k=0; ma[k++] ='$'; for(int i=0;i<l;i++){ ma[k++]='#'; ma[k++]=s[i]; } ma[k++]='#'; len=k; } void Manacher(){ Len[0]=0; int sum = 0,id=0,mx = 0; for(int i=1;i<len;i++){ if(i < mx) Len[i] = min(mx - i, Len[2 * id - i]); else Len[i] = 1; while(i-Len[i]>0 && i+Len[i]<len && ma[i - Len[i]]== ma[i + Len[i]]){ Len[i]++; } if(Len[i] + i > mx){ mx = Len[i] + i; id = i; } } return ; } bool check(){ int cout1=0,cout2=0; for(int i=1; i<len; i++){ if(Len[i]-i==0 && i!=1) pre1[++cout1]=i; if(i+Len[i]==len && i != len-1) pre2[++cout2]=i; } int l, r, mid; for(int i =1; i<=cout1; i++){ for(int j =1; j<= cout2; j++){ l = pre1[i] + Len[pre1[i]] ; r = pre2[j] - Len[pre2[j]] ; if(l > r) continue; mid=(l+r)>>1;//cout<<Len[mid]<<ma[mid]<<endl; if(Len[mid]*2>(r - l + 1)){ return true; } } } return false; } int main(){ int t; scanf("%d",&t); while(t--){ scanf("%s",s); int l=strlen(s); init(l); Manacher(); if(check()){printf("Yes\n");} else{printf("No\n");} } return 0; }
坚持自己所热爱的就是最好的