UVA10298 Power Strings
hash+乘法逆元+一点点数学知识
我们用取余法算出主串的hash,然后从小到大枚举子串的长度
显然,如果若干个子串的复制的hash值之和等于主串的hash值,那么答案就得到了。
然后我们计算子串(设子串长度为 i )的hash值:
hash=t*(1+base^i+base^2i+...+base^(len-i))
如果直接暴力求,那么显然会TLE
于是我们进行推导:
设: Si=1+base^i+base^2i+...+base^(len-i) S=Si*(base^i)=base^i+base^2i+base^3i+...+base^len 所以 S-Si=base^len-1=Si*(base^i-1) Si=(base^len-1)/(base^i-1)
我们利用快速幂可以很快地得出结果
但是涉及到取模除法运算,所以我们可以用乘法逆元,这里不详细讲了
根据费马小定理,base^i-1关于mod的乘法逆元为 (base^i-1)^(mod-2)
end.
然鹅更常用的判断循环子串方法在这里:point_here(本题升级版)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef unsigned long long ull; const int mod=1e9+7; const int base=131; char c[2000004]; inline ull ksm(int x,int y){ //快速幂 ull ans=1; for(;y;y>>=1){ if(y&1) ans=ans*(ull)x%mod; x=1LL*x*x%mod; } return ans%mod; } int main(){ do{ scanf("%s",c); if(c[0]=='.') return 0; int ans=1,len=strlen(c); ull tot=0,t=0; for(int i=0;i<len;++i) tot=(tot*base+(ull)c[i])%mod; //计算主串hash值 for(int i=1;i<=len/2;++i){ t=(t*base+(ull)c[i-1])%mod; //子串hash值 if(len%i) continue; //子串长度不被整除不可能有解 ull tmp=ksm(base,i)-1; ull s=(ksm(base,len)-1)*ksm(tmp,mod-2)%mod; if(t*s%mod==tot){ //刚好可以填充 ans=len/i; break; } } printf("%d\n",ans); }while(1); }