• Power string


    Problem Description
    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).
     

    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
     
    题目大意:给一个字符串S长度不超过10^6,求最大的n使得S由n个相同的子串连接而成。如:"ababab"则由n=3个"ab"连接而成,"aaaa"由n=4个"a"连接而成,"abcd"则由n=1个"abcd"连接而成。


    sol:本题实质是求最小循环节。注意:这里的循环节是要完全覆盖整个母串,而不只是母串的前缀。

    方法1用hash值求循环节。

    基本思路是:枚举循环节的长度,从S串划分出若干当前长度的子串,判断划分出来的每一小段hash值是否都相等,若相等,则该长度为最小循环节长度。当然枚举的长度要为S串长度的约数。如s="abcabcabcabc",len=1,"a"<>“b",1不是最小循环节长度。len=2,"ab"<>"ca",所以2不是最小循环节长度。len=3,"abc"="abc"="abc"="abc",3为最小循环节长度。

    代码实现:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 using namespace std;
     6 typedef unsigned long long LL;
     7 const LL base=131;
     8 const int N=1000010;
     9 int n;
    10 LL power[N],sum[N];
    11 bool check(LL v,int k)  //k为枚举的长度,判断s[1]~s[k]是否是循环节
    12 {
    13     for(register int i=1;i<=n;i+=k) //从s串的第一个位置开始,以k为长度划分
    14     {
    15         if(v!=sum[i+k-1]-sum[i-1]*power[k])//一路推过去每k位都应该值等于v,才是循环节 
    16             return 0;
    17     }
    18     return 1;
    19 }
    20 int main()
    21 {
    22     power[0]=1;
    23     for(register int i=1;i<=N-10;++i) //hash准备工作
    24         power[i]=power[i-1]*base;
    25     char s[N];
    26     while(scanf("%s",s+1))
    27     {
    28         if(s[1]=='.')break;
    29         n=strlen(s+1);
    30         sum[0]=0;
    31         for(register int i=1;i<=n;++i)
    32              sum[i]=sum[i-1]*base+LL(s[i]);
    33         for(register int i=1;i<=n;++i)
    34         {
    35             if(n%i) //循环节长度i,应为n的约数
    36                continue;
    37             LL expect=sum[i];//前i位的hash值 
    38             if(check(expect,i))//检测s串以每i位划分出来,每一段是否相等 
    39             {
    40                 printf("%d
    ",n/i);//检测成功,i为最小循环节长度,输出n/i的值 
    41                 break;
    42             }  
    43         }
    44     }
    45     return 0;
    46 }

    方法1的优化:枚举循环节的长度len,当然应是总长度的约数。用s的总长度-len求得一个数值num。判断S串的前num个字符的子串的hash值是否和后num个子串的hash值相等,若相等,len为最小循环节长度。如:s="abcabcabcabc",总长度为12,枚举长度len=3时,12-3=9,前9个字符和后9个字符相等,3为最小循环节长度。

    证明:len=3,我们把该串分为4段,s1 s2 s3 s4,若前9位和后9位hash值相同,则有s1 s2 s3=s2 s3 s4,则有s1=s2,s2=s3,s3=s4,即4段相等。

    方法2:KMP

    根据next[i]的定义,字符串中以i为结束位置的后i位与第一位开始的前i位相等,根据上面hash优化方法里的证明,且本题为完全覆盖,我们只要求出next[len]的值(len为s总长度),若len%(len-next(len)=0,则len-next(len)为循环节。

    s="abcabcabcabc",S的总长度len=12,求得next(12)的值为9,12%(12-9)=0,3为最小循环节长度。

     1 #include<cctype>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int nxt[1000010],m;
     6 char b[1000010];
     7 void get_next()
     8 {
     9     memset(nxt,0,sizeof(nxt));
    10     int j=0;
    11     for(int i=2;i<=m;i++)
    12     {
    13         while(j&&b[i]!=b[j+1])j=nxt[j];
    14         if(b[j+1]==b[i])nxt[i]=++j;
    15     }
    16 }
    17 int main()
    18 {
    19     while(1)
    20     {
    21         scanf("%s",b+1);
    22         if(b[1]=='.')break;
    23         m=strlen(b+1);
    24         get_next();
    25         //此题求的东西与后面的poi那个正好相反
    26         //例如"abcabcabc",易知next[9]=6,即字符串前缀长度为6的,正好等于后缀长度为6
    27         //如果存在循环节,则9-next[9]这个数字应该为9的约数
    28         //这样形如s1s2s3
    29         //等于       s1s2s3
    30         //即s2s3这一段等于s1s2这一样,于是我们将其等长划分后
    31         //s2=s1,s3=s2,于是s1=s2=s3 
    32         if(m%(m-nxt[m])==0)
    33            printf("%d
    ",m/(m-nxt[m]));
    34         else 
    35             puts("1");
    36     }
    37 }
  • 相关阅读:
    正整数分解质因数
    水仙花数
    键入任意整数,将之从小到大输出
    有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少
    输入某年某月某日,判断这一天是这一年的第几天?
    java 日期增加
    oracle数据库 ORA-01461: can bind a LONG value only for insert into a LONG column解决方案
    JAVA实现图片叠加效果
    JAVA_GET请求URL
    sqlserver-触发器-判断更新了哪个字段。
  • 原文地址:https://www.cnblogs.com/cutepota/p/12532694.html
Copyright © 2020-2023  润新知