题目描述
输入格式
输出格式
样例
数据范围与提示
保证 ∣S∣≤1e66,∑∣S∣≤5×106sumleft| S ight|leq 5 imes 10^6∑∣S∣≤5×106。
∑∣S∣ 表示的是单个测试点中所有数据 ∣S∣ 的总和。
天坑已补,在Manacher内一个for循环判断以该点为中心的最长回文子串能不能抵达原串的最右端即可
emmm,具体看代码
#include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #define ll long long using namespace std; const int maxn=1e6+20; char str[2*maxn],s[maxn]; int len1,len2,vis[maxn*2],p[maxn*2]; vector<int>vc; void init() { str[0]='$'; str[1]='#'; for(int i=0; i<len1; i++) { str[i*2+2]=s[i]; str[i*2+3]='#'; } len2=len1*2+2; str[len2]='*'; } void Manacher() { int id=0,mx=0; for(int i=1; i<len2; i++) { if(mx>i) p[i] =min(p[2*id-i],mx-i); else p[i]=1; for(; str[i-p[i]]==str[i+p[i]]; p[i]++); if(p[i]+i>mx) { mx=p[i]+i; id=i; } } // for(int i=0;i<len2;i++) printf("%c",str[i]);cout<<endl; // for(int i=0;i<len2;i++)printf("%d",p[i]);cout<<endl; memset(vis,0,sizeof(vis)); vc.clear(); for(int i=len1-1; i>=0; i--) { int r=p[(i+1)*2]/2; //以当前位置为中心的最长回文子串长度 if(i+r==len1) vis[i]=1; //能抵达最右边 合法 if(i+1-r==0) vis[i]=vis[i+r-1]; //较短,能抵达最左边,若合法,则说明能继续以最右端为中心翻转,此时最右边端点合法 if(vis[i]) vc.push_back(i+1); } for(int i=vc.size()-1; i>=0; i--) printf("%d%c",vc[i],i==0?' ':' '); } int main() { int t; cin>>t; while(t--) { cin>>s; len1=strlen(s); init(); Manacher(); } return 0; }
Hash + 二分
#include<bits/stdc++.h> using namespace std; const int N=1000005; int t,n,l[N]; char str[N]={'$'}; int main(){ scanf("%d",&t); while(t--){ scanf("%s",str+1); n=strlen(str+1); for(int i=1;i<=n;i++)l[i]=0; int id=0,mx=0; for(int i=1;i<=n;i++){ l[i]=max(1,min(mx-i+1,l[(id<<1)-i])); while(str[i-l[i]]==str[i+l[i]])l[i]++; if(mx<i+l[i]-1)mx=i+l[i]-1,id=i; } //for(int i=1;i<=n;i++)printf("%d ",l[i]);printf(" "); for(int i=2;i<n;i++){ int mid=i; while(mid<=n){ if(l[mid]<mid&&mid+l[mid]<=n)break; mid=(mid<<1)-1; } if(n<mid)printf("%d ",i); } printf("%d ",n); } return 0; }