【bzoj2795】[Poi2012]A Horrible Poem hash

Description

给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节。
如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到。

Input

第一行一个正整数n (n<=500,000),表示S的长度。
第二行n个小写英文字母,表示字符串S。
第三行一个正整数q (q<=2,000,000),表示询问个数。
下面q行每行两个正整数a,b (1<=a<=b<=n),表示询问字符串S[a..b]的最短循环节长度。

Output

依次输出q行正整数,第i行的正整数对应第i个询问的答案。

Sample Input

8

aaabcabc

3

1 3

3 8

4 8

Sample Output

1

3

5

HINT

Source

鸣谢 jiangzoi&oimaster


枚举约数是根号级的,算算复杂度好像过不了

有一个结论:若k是一个循环节,则pk也是一个循环节

于是把区间长度质因数分解,对于一个素数的指数ai,尽可能让它小,使得它再小就不是循环节为止。枚举指数是log级的。

O1判断是否是循环节可以hash

然后我写了个双hash,不过好像不需要?

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int SZ = 1000010;
const int mod1 = 1000000007;
const int mod2 = 998244353;

int pri[SZ];

char s[SZ];

int h1[SZ],h2[SZ],n;

int mi1[SZ],mi2[SZ];

int gethash1(int l,int r)
{
    int len = r - l + 1;
    return (int)((h1[r] - (LL)h1[l - 1] * mi1[len] % mod1 + mod1) % mod1);
}
int gethash2(int l,int r)
{
    int len = r - l + 1;
    return (int)((h2[r] - (LL)h2[l - 1] * mi2[len] % mod2 + mod2) % mod2);
}
bool check(int l,int r,int len) //[l,l + len - 1]  [r - len + 1,r]
{
    int x = r - l + 1 - len;
    int l1 = gethash1(l,l + x - 1),r1 = gethash1(r - x + 1,r);
    int l2 = gethash2(l,l + x - 1),r2 = gethash2(r - x + 1,r);
    return l1 == r1 && l2 == r2;
}


int ask_ans(int l,int r)
{
    int ans = r - l + 1;
    int x = r - l + 1;
    for(int i = 2;i * i <= x;i ++)
    {
        while(ans % i == 0 && check(l,r,ans / i))
            ans /= i;
        while(x % i == 0) x /= i;
    }
    if(x != 1 && check(l,r,ans / x))
        ans /= x;
    return ans;
}

int main()
{
    scanf("%d",&n);
    scanf("%s",s + 1);
    mi1[0] = mi2[0] = 1;
    for(int i = 1;i <= n;i ++) 
    {
        mi1[i] = (LL)mi1[i - 1] * 233 % mod1;
        mi2[i] = (LL)mi2[i - 1] * 10007 % mod2;
    }
    for(int i = 1;i <= n;i ++)
    {
        h1[i] = ((LL)h1[i - 1] * 233 % mod1 + s[i]) % mod1;
        h2[i] = ((LL)h2[i - 1] * 10007 % mod2 + s[i]) % mod2;
    }
    int q;
    scanf("%d",&q);
    while(q --)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",ask_ans(l,r)); 
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值