• 字符串的循环节--[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/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();
            //此题求的东西与后面的poi那个正好相反
            //例如"abcabcabc",易知next[9]=6,即字符串前缀长度为6的,正好等于后缀长度为6
            //如果存在循环节,则9-next[9]这个数字应该为9的约数
            //这样形如s1s2s3
            //等于       s1s2s3
            //即s2s3这一段等于s1s2这一样,于是我们将其等长划分后
            //s2=s1,s3=s2,于是s1=s2=s3 
            if(m%(m-nxt[m])==0)
               printf("%d
    ",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算法)

     

      

    /*
    列出以下性质:
    
    1、循环节一定是长度的约数(废话。。。)
    2、如果n是一个循环节,那么k*n也必定是一个循环节(关键所在)
    3、n是[l,r]这一段的循环节的充要条件是 ?[l,r-n]和[l+n,r]相同
    (利用这个性质我们在判断是否为循环节是可以做到O(1))
    
    我们从性质2开始,如果len是循环节,那么最小循环节一定是len的约数——是不是有想法了?
    枚举所有的质因子,不断除以原长并保证其仍是循环节,直到不能再小为止。
    复杂度O(nlogn)(常数很小)
    原文链接:https://blog.csdn.net/zeyu_king/article/details/41989279
    */
    #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;//对于数字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)
    //如果[a,b]这一段字符串存在循环节,其长度为L
    //则充要条件为[a,b-L]这一段等于[a+L,b]这一段
    //形如下图
    //1 2 3 4 5 
    //  1 2 3 4 5
    //1=2,2=3,3=4,4=5,于是它们就都相等了 
    {
        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)
            //对数字Len进行质因子分解,这个分解写得好奇怪 
            {
                son[++nn]=p[len];
                len=len/p[len];
               
            }
            len=r-l+1;
            for(int j=1;j<=nn;j++)
            {
                ll now=len/son[j];
                //将字符串分成son[j]段,每段长度为now 
                if(cheak(l,r,now))
                
                    len=now;
            }
            printf("%lld
    ",len);
        }
        return 0;
    }
    

      

  • 相关阅读:
    [Python图像处理]十.图像的灰度线性变换
    pyecharts实现散点图,词云图,饼状图,直方图
    pyecharts(柱状图)
    [Python图像处理]九.图像形态学相关运算
    如何排查内存泄漏
    如何进行SDK的测试
    分享两个实用的shell脚本实例
    分享一个实用脚本--一键获取linux内存、cpu、磁盘IO等信息
    Linux服务器问题排查思路及常用命令
    工作中最常用的Linux命令,排查问题必备
  • 原文地址:https://www.cnblogs.com/cutemush/p/12297466.html
Copyright © 2020-2023  润新知