【扩展kmp+最小循环节】HDU 4333 Revolving Digits

http://acm.hdu.edu.cn/showproblem.php?pid=4333

【题意】

  • 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的数比原数小的个数,相等的个数,大的个数

【思路】

  • 这个数很大,用字符串处理
  • 比较两个字符串的大小,一位一位很耗时,可以求出最长公共前缀,只比较最长公共前缀后一位
  • 每次将数的最后一位放到最后一位,如abcd变成dabc,cdab,bcda,相当于abcdabcd各个后缀的前四位
  • 这样就变成了求abcdabcd的每个后缀与abcd的最长公共前缀,用扩展KMP线性求
  • abab这种情况按上面的做法abababab会考察ab,ba,ab,ba,有重复的,是因为abab=(ab)^2,所以还要考虑最小循环节去重

【AC】

  1 #include<iostream>
  2 #include<cstring>
  3 #include<string>
  4 #include<cstdio>
  5 #include<algorithm>
  6 using namespace std;
  7 typedef long long ll;
  8 const int maxn=2e5+3;
  9 char s[maxn];
 10 char t[maxn];
 11 int nxt[maxn];
 12 int extend[maxn];
 13 int nxtval[maxn];
 14
 15
 16 void pre_EKMP(char x[],int m,int nxt[])
 17 {
 18     nxt[0]=m;
 19     int j=0;
 20     while(j+1<m && x[j]==x[j+1]) j++;
 21     nxt[1]=j;
 22     int k=1;
 23     for(int i=2;i<m;i++)
 24     {
 25         int p=nxt[k]+k-1;
 26         int L=nxt[i-k];
 27         if(i+L<p+1) nxt[i]=L;
 28         else
 29         {
 30             j=max(0,p-i+1);
 31             while(i+j<m && x[i+j]==x[j]) j++;
 32             nxt[i]=j;
 33             k=i;
 34         }
 35     }
 36 }
 37
 38 void EKMP(char x[],int m,char y[],int n,int nxt[],int extend[])
 39 {
 40     pre_EKMP(x,m,nxt);//子串
 41     int j=0;
 42     while(j<n && j<m &&x[j]==y[j]) j++;
 43     extend[0]=j;
 44     int k=0;
 45     for(int i=1;i<n;i++)
 46     {
 47         int p=extend[k]+k-1;
 48         int L=nxt[i-k];
 49         if(i+L<p+1) extend[i]=L;
 50         else
 51         {
 52             j=max(0,p-i+1);
 53             while(i+j<n && j<m && y[i+j]==x[j]) j++;
 54             extend[i]=j;
 55             k=i;
 56         }
 57     }
 58 }
 59
 60 void kmp_pre(char x[],int m,int nxtval[])
 61 {
 62     int i,j;
 63     j=nxtval[0]=-1;
 64     i=0;
 65     while(i<m)
 66     {
 67         if(j==-1||x[i]==x[j])
 68         {
 69             i++;
 70             j++;
 71             if(x[i]!=x[j]) nxtval[i]=j;
 72             else  nxtval[i]=nxtval[j];
 73         }
 74         else j=nxtval[j];
 75     }
 76
 77 }
 78
 79 void NextVal(char *T)
 80 {
 81     int i=0,j=-1;
 82     nxtval[0]=-1;
 83     int Tlen=strlen(T);
 84     while(i<Tlen)
 85     {
 86         if(j==-1||T[i]==T[j])
 87         {
 88             i++;
 89             j++;
 90             if(T[i]!=T[j]) nxtval[i]=j;
 91             else  nxtval[i]=nxtval[j];
 92         }
 93         else j=nxtval[j];
 94     }
 95 }
 96
 97
 98 int main()
 99 {
100     int T;
101     scanf("%d",&T);
102     int cas=0;
103     while(T--)
104     {
105         scanf("%s",s);
106         strcpy(t,s);
107         strcat(s,t);
108         int a,b,c;
109         a=b=c=0;
110         int ls=strlen(s);
111         int lt=strlen(t);
112         EKMP(t,lt,s,ls,nxt,extend);
113         kmp_pre(t,lt,nxtval);
114         int p=lt-nxtval[lt];
115         int tmp=1;
116         if(lt%p==0) tmp=lt/p;
117         for(int i=0;i<lt;i++)
118         {
119             if(extend[i]==lt) a++;
120             else if(s[i+extend[i]]>t[extend[i]]) c++;
121             else b++;
122         }
123         printf("Case %d: %d %d %d\n",++cas,b/tmp,a/tmp,c/tmp);
124     }
125     return 0;
126 }

扩展kmp求最小循环节方法一:kmp预处理

扩展kmp计算最小循环节方法二:利用已知的next数组

【知识点】

扩展kmp的next数组与kmp数组的next含义不同,是字符串s的所有后缀和s本身的最长公共前缀

【坑点】

做这道题踩了各种坑

  • strcat函数的用法:strcat(s,s)是错误的,会T,strcat的两个参数传的是指针,就是s在内存里面的位置,这里两个s是同一个东西 第一个s变长的时候,第二个s也会变长,然后就没完没了了
  • kmp求最小循环节这里
  • void kmp_pre(char x[],int m,int nxtval[])
    {
        int i,j;
        j=nxtval[0]=-1;
        i=0;
        while(i<m)
        {
            while(-1!=j && x[i]!=x[j]) j=nxtval[j];
            nxtval[++i]=nxtval[++j];
        }
    }

    会WA

  • 正确的是这个
  • void kmp_pre(char x[],int m,int nxtval[])
    {
        int i,j;
        j=nxtval[0]=-1;
        i=0;
        while(i<m)
        {
            if(j==-1||x[i]==x[j])
            {
                i++;
                j++;
                if(x[i]!=x[j]) nxtval[i]=j;
                else  nxtval[i]=nxtval[j];
            }
            else j=nxtval[j];
        }
    }
时间: 2024-10-25 08:18:54

【扩展kmp+最小循环节】HDU 4333 Revolving Digits的相关文章

扩展KMP,附上例题(HDU - 4333 Revolving Digits)

给出模板串S和串T,长度分别为Slen和Tlen,在线性时间内,对于每个S[i](0<=i<Slen),求出S[i..Slen-1]与T的 最长公共前缀长度,记为extend[i],extend[i]存放s[i]开始与T的最长公共前缀长度. 例子 a a a a a a a b b b a a a a a c extend 5 4 3 2 1 0 0 0 0 0 HDU - 4333 Revolving Digits Time Limit: 3000/1000 MS (Java/Others)

Revolving Digits(扩展Kmp+最小循环节)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333 Revolving Digits Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 28267    Accepted Submission(s): 6363 Problem Description One day Silence is

HDU 4333 Revolving Digits 扩展KMP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333 题意:给以数字字符串,移动最后若干位到最前边,统计得到的数字有多少比原来大,有多少和原来相同,有多少比原来的小. 思路:拓展KMP中的next数组标记的是子串和母串的公共前缀的长度,要将字符串长度变成原来二倍,这样如果变换后不是完全相同的数字也即公共前缀长度大于等于字符串长度,那么字母串公共前缀的下一位的大小比较就是题目所要求的比较.由于相同的数字串只算一次,则只要统计比较第一个"循环节"

HDU - 4333 Revolving Digits(拓展kmp+最小循环节)

1.给一个数字字符串s,可以把它的最后一个字符放到最前面变为另一个数字,直到又变为原来的s.求这个过程中比原来的数字小的.相等的.大的数字各有多少. 例如:字符串123,变换过程:123 -> 312 -> 231 -> 123 因为:312>123, 231>123, 123=123 所以答案是:0 1 2 2.令str1=s,str2=s+s,然后str1作为子串,str2作为主串,进行扩展kmp求出str2[i...len2-1]与str1[0...len1-1]的最长

hdu4333 Revolving Digits(扩展kmp+kmp最小循环节)

题目链接:点击打开链接 题意描述:给定一个字符串,可以把字符串的后x位移到字符串前面组成一个新的字符串?问对于所有的新组成的字符串中去掉重复的之后,比原串小的个数,等于的个数,大于的个数? 解题思路:扩展KMP(next1[i]数组含义:x[i···len-1]与x[0···len-1]的最长公共前缀) 分析:首先我们把字符串s复制一遍接到原字符串后面形成ss,这样在ss中以i(i>=0&&i<len)为起点的长度为len的字符串就是所有可能的新字符串: 讲到这里,问题转变为在

扩展KMP - HDU 4333 Revolving Digits

Revolving Digits Problem's Link: http://acm.hdu.edu.cn/showproblem.php?pid=4333 Mean: 给你一个字符串,你可以将该字符串的任意长度后缀截取下来然后接到最前面,让你统计所有新串中有多少种字典序小于.等于.大于原串. analyse: KMP的经典题. 首先我们将原串扩展成两倍,算一遍扩展KMP(自匹配),时间复杂度O(n). 这样一来,我们就得到了eKMP[i],eKMP[i]代表s[i...len-1]与s的最长

HDU 3746 Cyclic Nacklace (KMP最小循环节)

Cyclic Nacklace Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11264    Accepted Submission(s): 4821 Problem Description CC always becomes very depressed at the end of this month, he has check

UVAlive 3026 KMP 最小循环节

KMP算法: 一:next数组:next[i]就是前面长度为i的字符串前缀和后缀相等的最大长度,也即索引为i的字符失配时的前缀函数. 二:KMP模板 1 /* 2 pku3461(Oulipo), hdu1711(Number Sequence) 3 这个模板 字符串是从0开始的 4 Next数组是从1开始的 5 */ 6 #include <iostream> 7 #include <cstring> 8 using namespace std; 9 10 const int m

hdu 4333 Revolving Digits

Revolving Digits http://acm.hdu.edu.cn/showproblem.php?pid=4333 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description One day Silence is interested in revolving the digits of a positive integer. In th