最小表示&Manacher
在一个字符串中找到一个循环同构使得该循环同构字典序最小
朴素算法:
暴力枚举每个串并比较O(n^2),实现时可用双指针,
force:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; template <typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ putchar('-'); a=-a; } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const int maxn=3e5+5; int n,a[maxn*2]; int main(){ read(n); for(int i=1;i<=n;i++){ read(a[i]); a[n+i]=a[i]; } int i=1,j=2;//i表示可能是最小表示的位置 while(i<=n&&j<=n){ if(a[j]<a[i])i=j++; else if(a[j]>a[i])j++; else{ int k=1; while(a[i+k]==a[j+k]&&k<n)k++; //向后扫描找到长度内第一个 不相等的字符 if(k<n){ if(a[j+k]<a[i+k])i=j++; else j++; } } } for(int p=0;p<n;p++){ write(a[i+p]);putchar(' '); } return 0; }
然后我们考虑为什么很慢,O(n)的比较是不能避免的了,所以我们从i的移动上做文章,
我们不难发现,i在移动时,如果a[i+k]!=a[j+k]
不妨设a[i+k]<a[j+k]:
那么假设a[j]~a[j+k]中有答案,那么一定是无论怎么比较都是最小的,所以a[j]~a[j+k]一定没有答案
证明结束,直接j=j+k+1
ac:
#include<cstdio> #include<cstring> using namespace std; const int maxn=3e5+5; template <typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ putchar('-'); a=-a; } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } int n,a[maxn*2]; int main(){ read(n); for(int i=1;i<=n;i++){ read(a[i]); a[i+n]=a[i]; } int i=1,j=2;//指针i,j表示可能的同构串的位置 while(i<=n&&j<=n){ int k=0; while(a[i+k]==a[j+k]&&k<n)k++; if(k==n)break;//此时发现只有一个字符,直接结束 if(a[i+k]<a[j+k]){ //此时a[j]~a[j+k]都不可能是最小表示 j=j+k+1;//注意别写反了,此处的变换是排除!确实不可能!的答案 if(i==j)j++; } else { i=i+k+1; if(j==i)i++; } } if(i>n)i=j; for(int p=0;p<n;p++){ write(a[i+p]);putchar(' '); } return 0; }
二、Manacher:
求解字符串中最长回文子串
朴素算法:
枚举左右端点判断,O(n^3)
force 1:
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<cmath> using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ putchar('-'); a=-a; } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const int maxn=11000000; char ch[maxn]; inline bool huiwen(int l,int r){ while(ch[l]==ch[r]&&l<=r){l++;r--;} return l>r; } int main(){ scanf("%s",ch+1); int len=strlen(ch+1); for(int i=len;i;i--){ bool fd=0; for(int j=1;j<=len-i+1;j++){ if(huiwen(j,j+i-1)){ write(i); fd=1; break; } } if(fd)break; } return 0; }
枚举中点判断,O(n^2)
force 2:
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<cmath> using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ putchar('-'); a=-a; } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const int maxn=11000000; char ch[maxn]; int n,ans; int main(){ scanf("%s",ch+1); n=strlen(ch+1); for(int i=1;i<=n;i++){ //奇数串 int len=1; while(ch[i+len]==ch[i-len]&&i-len&&i+len<=n)len++; ans=max(ans,(len-1)<<1|1); //偶数串 len=0; while(ch[i-len]==ch[i+len+1]&&i-len&&i+len<n)len++; ans=max(ans,len<<1); } write(ans); return 0; }