【题解】【P3538 [POI2012]OKR-A Horrible Poem】
Analysis
- 首先循环节和border一一对应,所以判断某个长度x是否为循环节,可以直接判断len-x是否是border,判断方法有两种,hash或者后缀数组都可以。
- 因此有一种做法:从小到大枚举询问子串长度的所有约数,并利用上述判断方法O(1)判断,约数可以预处理出来。总复杂度O(q∗最大约数个数),但是有个点会T
- 上述方法复杂度近似O(q√n),考虑优化。先提出一个引理:字符串的所有循环节都是最小循环节的倍数。(证明见Tip)。那么len一定是最小循环节的倍数,然后枚举len的所有质因子,若len/p可以作为循环节,那么len/p一定是最小循环节的倍数,令len/=p。将len的质因子枚举完后,最后的len显然就是答案。一个数质因子的个数是log级别的,可以通过本题。可以通过筛法预先筛出每个数的质因子集合。
Tip:我们可以将字符串的循环节类比成周期函数,设存在两个循环节x,y,则f(i)=f(i+x)=f(i+y),那么f(i)=f(i+y-x),进一步f(i)=f(i+y%x)。可以发现这个过程与辗转相除法类似。最终可以推出gcd(x,y)是循环节,因此,若存在一个循环节不是最小循环节的倍数,那么一定存在一个比最小循环节还要小的循环节,就会出现矛盾。
Code
| #include<bits/stdc++.h> |
| using namespace std; |
| |
| inline int read() |
| { |
| register int x=0,w=1; |
| register char ch=getchar(); |
| while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); |
| if(ch=='-') {ch=getchar();w=-1;} |
| while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} |
| return x*w; |
| } |
| inline void write(int x) |
| { |
| if(x<0) putchar('-'),x=~(x-1); |
| if(x>9) write(x/10); |
| putchar('0'+x%10); |
| } |
| const int N=5e5+100; |
| char s[N]; |
| int rk[N],y[N<<1],t[N],n,m=128,q,sa[N],h[N],st[N][20],mxlg[N]; |
| vector<int>d[N]; |
| void getsa() |
| { |
| for(int i=1;i<=n;++i) t[rk[i]=s[i]]++; |
| for(int i=1;i<=m;++i) t[i]+=t[i-1]; |
| for(int i=1;i<=n;++i) sa[t[rk[i]]--]=i; |
| for(int k=1;;k<<=1) |
| { |
| int cnt=0; |
| for(int i=n-k+1;i<=n;++i) y[++cnt]=i; |
| for(int i=1;i<=n;++i) if(sa[i]>k) y[++cnt]=sa[i]-k; |
| for(int i=1;i<=m;++i) t[i]=0; |
| for(int i=1;i<=n;++i) t[rk[i]]++; |
| for(int i=1;i<=m;++i) t[i]+=t[i-1]; |
| for(int i=n;i;--i) sa[t[rk[y[i]]]--]=y[i],y[i]=rk[i]; |
| for(int i=1;i<=n;++i) rk[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?rk[sa[i-1]]:rk[sa[i-1]]+1; |
| m=rk[sa[n]];if(m==n) break; |
| } |
| for(int i=1,j,k=0;i<=n;h[rk[i++]]=k) for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k); |
| } |
| void stpre() |
| { |
| for(int i=2;i<=n;++i) mxlg[i]=(1<<mxlg[i-1]+1)<=i?mxlg[i-1]+1:mxlg[i-1]; |
| for(int i=1;i<=n;++i) st[i][0]=h[i]; |
| for(int j=1;j<=mxlg[n];++j) |
| for(int i=1;i+(1<<j)-1<=n;++i) |
| st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]); |
| } |
| int lcp(int x,int y) |
| { |
| if(rk[x]>rk[y]) swap(x,y); |
| int k=mxlg[rk[y]-rk[x]]; |
| return min(st[rk[x]+1][k],st[rk[y]-(1<<k)+1][k]); |
| } |
| int p[N],num,v[N]; |
| void init() |
| { |
| for(int i=2;i<=n;++i) |
| { |
| if(v[i]==0){ |
| v[i]=i;p[++num]=i;d[i].push_back(i); |
| |
| } |
| for(int j=1;p[j]<=n/i;++j) |
| { |
| v[p[j]*i]=p[j];d[p[j]*i].push_back(p[j]);for(int k=0;k<d[i].size();++k) d[p[j]*i].push_back(d[i][k]); |
| if(i%p[j]==0) break; |
| } |
| } |
| } |
| signed main() |
| { |
| scanf("%s",s+1); |
| getsa(); |
| stpre(); |
| q=read(); |
| for(int i=1;i<=q;++i) |
| { |
| int l=read(),r=read(),len=r-l+1; |
| int x=len; |
| for(int j=0;j<d[len].size();++j) |
| { |
| int w=len-x/d[len][j]; |
| if(lcp(l,r-w+1)>=w) x/=d[len][j]; |
| } |
| write(x);puts(""); |
| } |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效