[Poi2012]A Horrible Poem

先放个简单版的

Pku2406 Power Strings

Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative integer is defined in the normal way: a^0 = "" (the empty string) and a^(n+1) = a*(a^n).
求一个字符串由多少个重复的子串连接组成,例如ababab由3个ab连接而成,因此答案为3,又例如abcd由1个abcd连接而成,因此答案为1
Input
Each test case is a line of input representing s, a string of printable characters. The length of s will be at least 1 and will not exceed 1 million characters. A line containing a period follows the last test case.
Output
For each s you should print the largest n such that s = a^n for some string a.
Sample Input
abcd
aaaa
ababab
.
Sample Output
1
4
3

Sol1:Hash

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
typedef unsigned long long LL;
const LL base=131;
const int N=1000010;
int n;
LL power[N],sum[N];
bool check(LL v,int k)  //判断s[1]~s[k]是否是循环节
{
    for(register int i=1;i<=n;i+=k){
        if(v!=sum[i+k-1]-sum[i-1]*power[k]) return 0;
    }
    return 1;
}
int main()
{
    power[0]=1;
    for(register int i=1;i<=N-10;++i) //hash准备工作
        power[i]=power[i-1]*base;
    char s[N];
    while(scanf("%s",s+1)){
        if(s[1]==‘.‘)break;
        n=strlen(s+1);
        sum[0]=0;
        for(register int i=1;i<=n;++i) sum[i]=sum[i-1]*base+LL(s[i]);
        for(register int i=1;i<=n;++i){
            if(n%i)continue;
            LL expect=sum[i];
            if(check(expect,i)){
                printf("%d\n",n/i);
                break;
            }
        }
    }
    return 0;
}

Sol2:Kmp算法

#include<cctype>
#include<cstdio>
#include<cstring>
using namespace std;
int nxt[1000010],m;
char b[1000010];
void get_next()
{
    memset(nxt,0,sizeof(nxt));
    int j=0;
    for(int i=2;i<=m;i++)
    {
        while(j&&b[i]!=b[j+1])j=nxt[j];
        if(b[j+1]==b[i])nxt[i]=++j;
    }
}
int main()
{
    while(1)
    {
        scanf("%s",b+1);
        if(b[1]==‘.‘)break;
        m=strlen(b+1);
        get_next();
        if(m%(m-nxt[m])==0)printf("%d\n",m/(m-nxt[m]));
        else puts("1");
    }
}

  

给出一个由小写英文字母组成的字符串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

Sol:设Len为所求的区间长度,然后对Len这个数字做质因子分解

例如Len=20时,20=2*2*5

首先取出长度为20/2=10的字符串的前缀及后缀,看其是否相等,如果相等则Len=10

然后取出长度为10/2=5的字符串的前缀及后缀,看是否相等,如果不等,则Len不变

然后取出Len=10/5=2的字符串的前缀及后缀,看是否相等。

这个思路其实是求字符串的循环节,可参考Period那个题(Kmp算法)

#include<bits/stdc++.h>
#define ll long long
#define N 500005
#define mod 19260817
using namespace std;
inline void read(ll &x)
{
    ll datta=0;char chchc=getchar();bool okoko=0;
    while(chchc<‘0‘||chchc>‘9‘){if(chchc==‘-‘)okoko=1;chchc=getchar();}
    while(chchc>=‘0‘&&chchc<=‘9‘){datta=datta*10+chchc-‘0‘;chchc=getchar();}
    x=okoko?-datta:datta;
}
ll p[N],n,m,q[N],l,r,hx[N],base=29;
ll prime[N],pnum,len,son[N],nn;
char c[N];
bool h[N];
void pre()
{
    for(int i=2;i<=n;i++)
	{
        if(!h[i])
		{
            prime[++pnum]=i;
            p[i]=i;
        }
        for(int j=1;j<=pnum&&i*prime[j]<=n;j++)
		{
            h[i*prime[j]]=true;
            p[i*prime[j]]=prime[j];
            if(!i%prime[j])break;
        }
    }
	q[0]=1;
    for(int i=1;i<=n;i++)
	     q[i]=(q[i-1]*base)%mod;
    for(int i=1;i<=n;i++)
	{
        hx[i]=hx[i-1]*base+c[i]-‘a‘+1;
        hx[i]=hx[i]%mod;
    }
}
ll fk(ll a,ll b)
{
    ll num=hx[b]-(hx[a-1]*q[b-a+1])%mod;
    num=(num+mod)%mod;
    return num;
}
bool cheak(ll a,ll b,ll l)
//取字符串中长度为L的前缀与后缀
{
    if(fk(a,b-l)==fk(a+l,b))
	    return true;
    else
	    return false;
}
int main()
{
    read(n);
    scanf("%s",c+1);
    pre();
	read(m);
    for(int i=1;i<=m;i++){
        read(l),read(r);
        len=r-l+1,nn=0;
        while(len!=1)
		{
            son[++nn]=p[len];
            len=len/p[len];

        }
        len=r-l+1;
        for(int j=1;j<=nn;j++)
		{
            ll now=len/son[j];
            if(cheak(l,r,now))
			//如果找出循环节,则Len变成Now
			    len=now;
        }
        printf("%lld\n",len);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/cutemush/p/12297466.html

时间: 2024-10-09 21:19:50

[Poi2012]A Horrible Poem的相关文章

[BZOJ2795][Poi2012]A Horrible Poem

2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 261  Solved: 150[Submit][Status][Discuss] Description 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. Input 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小

2795: [Poi2012]A Horrible Poem

2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 484  Solved: 235[Submit][Status][Discuss] Description 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. Input 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小

【BZOJ2795】[Poi2012]A Horrible Poem hash

[BZOJ2795][Poi2012]A Horrible Poem 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),表示询

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

...字符串hash. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 500009; const ull P = 1000173169; char S[maxn]; int check[maxn], prime[maxn], N = 0, n; ull H[maxn], K[maxn]; void init() { memset(check, 0, si

【bzoj2795】[Poi2012]A Horrible Poem Hash+分解质因数

题目描述 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. 输入 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小写英文字母,表示字符串S.第三行一个正整数q (q<=2,000,000),表示询问个数.下面q行每行两个正整数a,b (1<=a<=b<=n),表示询问字符串S[a..b]的最短循环节长度. 输出 依次输出q行正整数,第i行的正整数对应第i

【bzoj2795】【Poi2012】A Horrible Poem

题解: 询问区间的整循环节 设区间长度为$n$ 如果有循环节长为$x$和$y$,那由斐蜀定理得$gcd(x,y)$也一定为一个循环节: 假设最小的循环节长为$mn$,那么对于任何循环节长$x$,一定$mn | x$ , 否则$gcd(mn,x)<mn$矛盾 推出$\frac{n}{x} | \frac{n}{mn}$ 所以每次提出$n$的一个质因子$p$,考虑是否可以分成$p$段,如果可以$n=\frac{n}{p}$继续找: 最后得出来的$n$就是最短的循环节: 分解质因子可以$O(n)$线筛

bzoj-2795 A Horrible Poem

题意: 给出一个长度为N的字符串,有Q次询问: 每次询问给出一个区间,求区间最短循环节长度: N<=500000,Q<=2000000: 题解: 这题数据范围简直丧病= =渣电脑3s真的能跑出来吗... 不过这题在BZ上是可做的,50s我的程序10s出解了: 首先这问题画一画发现它绝对不是什么数据结构能维护的,因为这东西毫无可并性: 硬说的话多个相同的的连在一起的循环节可以合并变长,然并卵: 所以如果考虑暴力一些呢? 发现循环节长度一定是区间长度n的约数,可以枚举n的约数: 验证利用RKhas

BZOJ_2795_[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 依次输

A Horrible Poem

https://loj.ac/problem/10038 题目描述 给出一个字符串S,以及q次询问,每次询问这个字符串子串的最短循环节. 思路 这道题是毒瘤题,一定是毒瘤题,至少loj的数据是.我有两个思路,一个93分,一个不加快读97分,加快读满分.  首先,很显然,对于一个字符串求最小循环节,枚举为其长度因数的前缀.但这样显然会T,我们考虑优化. ①我们考虑可以通过预处理,快速求出一段子串内所有字母的个数,而最小循环节中的字母个数一定是所有字母数的公因数,那么最小循环节的个数一定是这个公因数