# 10038. 「一本通 2.1 练习 4」A Horrible Poem

【题目描述】

给出一个由小写英文字母组成的字符串 $S$,再给出 $q$ 个询问,要求回答 $S$ 某个子串的最短循环节。

如果字符串 $B$ 是字符串 $A$ 的循环节,那么 $A$ 可以由 $B$ 重复若干次得到。

【算法】

-首先对于长度为 $len$ 的子串,循环节长度为 $x$ 的充要条件:$[1,len-x]$串的哈希值等于 $[x+1,len]$ 串的哈希值。

-一开始暴力枚举最短循环节长度(n的约数),然后T了一半。

有两种方法进行优化:

1、循环节个数必然是子串各个字母个数和子串长度的公约数,可以枚举循环节个数此时bzoj就能过了,但是loj还是会T。(93分) $O(q\sqrt{n})$

2、正解:假设最短循环节长度为len则原串长度显然为len*k。若只考虑k,并且将k的质因数依次分解,每次试除k,则得到的$k^。$和len的乘积仍是循环节,利用这个性质。依次用质因数 $i$ 试除n,若除去后仍是循环节,说明i属于k,将其除去,结果就留下了len。

【代码1】

#include <bits/stdc++.h>
#define P 131
#define ULL unsigned long long
using namespace std;
int n,q,a,b,len;
int rec[500010][26];
ULL h[500010],p[500010];
char s[500010];
inline int read() {
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); }
return x*f;
}
void parse() {
p[0]=1;
for(int i=1;i<=n;i++) p[i]=p[i-1]*P,h[i]=h[i-1]*P+(ULL)s[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=26;j++)
rec[i][j]=rec[i-1][j]+(s[i]-'a'+1==j);
}
int gcd(int x,int y) {
return y?gcd(y,x%y):x;
}
bool valid(int l) {
return h[b]-h[a+l-1]*p[len-l]==h[a+(len/l-1)*l-1]-h[a-1]*p[len-l];
}
int main() {
n=read();
gets(s+1);
q=read();
parse();
while(q--) {
a=read(),b=read();
len=b-a+1;
int num=len,ans=0;
for(int i=1;i<=26;i++) num=gcd(num,rec[b][i]-rec[a-1][i]);
for(int i=1;i*i<=num;i++) {
if(num%i==0) {
if(valid(len/(num/i))) { ans=max(ans,num/i); break; }
if(valid(len/i)) ans=max(ans,i);
}
}
printf("%d\n",len/ans);
}
return 0;
}

【ac代码】

#include <bits/stdc++.h>
#define N 500010
#define P 131
#define ULL unsigned long long
using namespace std;
int n,q,len,tot;
ULL h[N],p[N];
int prime[N],minp[N];
char s[N];
inline int read() {
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); }
return x*f;
}
void parse() {
for(int i=2;i<=n;i++) {
if(!minp[i]) {
prime[++tot]=i;
minp[i]=i;
}
for(int j=1;j<=tot;j++) {
if(prime[j]>minp[i]||prime[j]*i>n) break;
minp[prime[j]*i]=prime[j];
}
}
p[0]=1;
for(int i=1;i<=n;i++)
h[i]=h[i-1]*P+(ULL)s[i],p[i]=p[i-1]*P;
}
bool valid(int a,int b,int l) {
return h[b]-h[a+l-1]*p[len-l]==h[a+(len/l-1)*l-1]-h[a-1]*p[len-l];
}
int main() {
n=read();
gets(s+1);
q=read();
parse();
while(q--) {
int a,b,ans,tmp;
a=read(),b=read();
len=tmp=ans=b-a+1;
while(tmp!=1) {
int t=minp[tmp];
while(tmp%t==0&&valid(a,b,ans/minp[tmp])) tmp/=t,ans/=t;
while(tmp%t==0) tmp/=t;
}
printf("%d\n",ans);
}
return 0;
}

A Horrible Poem (字符串hash+数论)的更多相关文章

  1. 洛谷P3538 [POI2012]OKR-A Horrible Poem [字符串hash]

    题目传送门 A Horrible Poem 题目描述 Bytie boy has to learn a fragment of a certain poem by heart. The poem, f ...

  2. 【hash】A Horrible Poem

    [题目链接] # 10038. 「一本通 2.1 练习 4」A Horrible Poem [参考博客] A Horrible Poem (字符串hash+数论) [题目描述] 给出一个由小写英文字母 ...

  3. bzoj 2795 [Poi2012]A Horrible Poem hash+数论

    2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 640  Solved: 322[Subm ...

  4. BZOJ 2795: [Poi2012]A Horrible Poem( hash )

    ...字符串hash. 假如长度x是一个循环节, 那么对于任意n(x | n)也是一个循环节. 设当前询问区间[l, r]长度为len = ∏piai, 最终答案ans = ∏piai' ,我们只需枚 ...

  5. 【BZOJ2795】[Poi2012]A Horrible Poem hash

    [BZOJ2795][Poi2012]A Horrible Poem Description 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串 ...

  6. 2795: [Poi2012]A Horrible Poem

    2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 484  Solved: 235[Subm ...

  7. [BZOJ2795][Poi2012]A Horrible Poem

    2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 261  Solved: 150[Subm ...

  8. #10038.A Horrible Poem

    #10038.A Horrible Poem 题目传送门 思路解析 既然这道题目在hash板块里,那么自然就可以想到用hash做这道题目. 首先我们可以用hash数组存储字符串的前缀的hash值. 因 ...

  9. P3538 [POI2012]OKR-A Horrible Poem

    P3538 [POI2012]OKR-A Horrible Poem hash+线性筛 题解 <----这篇写的不错(其实是我懒得码字了qwq) UVA10298 Power Strings 的 ...

随机推荐

  1. vue 2.x 的 v-bind 指令的 .prop 事件修饰符详解

    vue 官方文档对 .prop 修饰符的解释是: 使用例子: 那么,具体的原理和用法是什么呢?这要从 html 的 DOM node 说起. 在 html 标签里,我们可以定义各种 attribute ...

  2. 【译】OkHttp3 拦截器(Interceptor)

    一,OkHttp 拦截器介绍(译自官方文档) 官方文档:https://github.com/square/okhttp/wiki/Interceptors 拦截器是 OkHttp 提供的对 Http ...

  3. 邻居子系统输出 之 neigh_output、neigh_hh_output

    概述 ip层在构造好ip头,检查完分片之后,会调用邻居子系统的输出函数neigh_output进行输出,输出分为有二层头缓存和没有两种情况,有缓存时调用neigh_hh_output进行快速输出,没有 ...

  4. State Threads之编程注意事项

    原文: Programming Notes 1. 移植 State Thread 库可移植到大多数类 UNIX 平台上,但是该库有几个部分需要依赖于平台特性,以下列出了这些部分: 线程上下文初始化. ...

  5. Mybatis多值传递的方式

    一共有三种方式 1.参数传入Map 2参数使用@params 3.直接使用时用#{0},#{2} 参考网址 :https://www.2cto.com/database/201409/338155.h ...

  6. linux读xml文件问题

    由于从配置文件中读取到的路径名的最后多了个0x0A,害我折腾了半天. 提示也很奇葩: I/O warning : failed to load external entity 关键是从这个提示看不出是 ...

  7. JDBC的URL

    JDBC的URL=协议名+子协议名+数据源名. 协议名总是“jdbc”. 子协议名由JDBC驱动程序的编写者决定. 数据源名也可能包含用户与口令等信息:这些信息也可单独提供. 几种常见的数据库连接 o ...

  8. IPV6基础

    Pv6与IPv4的区别 Pv6报文与IPv4报文差别就两个地方: 一个是数据链路层(以太网协议)中协议类型,IPv4是0x0800,IPv6是0x86DD 另一个是IPv6 Header是40字节,I ...

  9. 作为web开发人员,你必须要知道的问题! (持续更新)

    GET 和 POST 的区别 GET请注意,查询字符串(名称/值对)是在 GET 请求的 URL 中发送的:/test/demo_form.asp?name1=value1&name2=val ...

  10. Servlet 表单数据 接收get post 参数实例

    Servlet 表单数据 很多情况下,需要传递一些信息,从浏览器到 Web 服务器,最终到后台程序.浏览器使用两种方法可将这些信息传递到 Web 服务器,分别为 GET 方法和 POST 方法. GE ...