杭电4333扩展kmp

来源:互联网 发布:js 获取cookie的值 编辑:程序博客网 时间:2024/06/06 10:44

Revolving Digits

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1618    Accepted Submission(s): 467


Problem Description
One day Silence is interested in revolving the digits of a positive integer. In the revolving operation, he can put several last digits to the front of the integer. Of course, he can put all the digits to the front, so he will get the integer itself. For example, he can change 123 into 312, 231 and 123. Now he wanted to know how many different integers he can get that is less than the original integer, how many different integers he can get that is equal to the original integer and how many different integers he can get that is greater than the original integer. We will ensure that the original integer is positive and it has no leading zeros, but if we get an integer with some leading zeros by revolving the digits, we will regard the new integer as it has no leading zeros. For example, if the original integer is 104, we can get 410, 41 and 104.
 

Input
The first line of the input contains an integer T (1<=T<=50) which means the number of test cases.
For each test cases, there is only one line that is the original integer N. we will ensure that N is an positive integer without leading zeros and N is less than 10^100000.
 

Output
For each test case, please output a line which is "Case X: L E G", X means the number of the test case. And L means the number of integers is less than N that we can get by revolving digits. E means the number of integers is equal to N. G means the number of integers is greater than N.
 

Sample Input
1341
 

Sample Output
Case 1: 1 1 1
 

Source
2012 Multi-University Training Contest 4
 

几天没写博客了。。看的kmp想吐血了
这个题目纠结了两天了,开始看人家的代码看不懂。。
后来自己写了一个又超时,我发现很多求kmp的next数组第一个都是-1,我偏偏看了是0的代码(感觉容易理解),
这题主要求出最短循环结的长度就好办了,然后再求扩展kmp的next数组的值,最后比较。。

先附上超时代吗:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int MAXN = 10^100000*2;char str[MAXN];int next[MAXN];int nnext[MAXN];void makeNext(){    int q,i;    nnext[0]=0;    for(q=0,i=1;i<strlen(str);++i)    {        while(q>0 && str[i]!=str[q])        {            q=nnext[q-1];        }        if(str[i]==str[q])        {            ++q;        }        nnext[i]=q;    }}/*void SetPrefix(const char *Pattern, int prefix[]){     int len=strlen(Pattern);//模式字符串长度。     prefix[0]=0;     for(int i=1; i<len; i++)     {         int k=prefix[i-1];         //不断递归判断是否存在子对称,k=0说明不再有子对称,Pattern[i] != Pattern[k]说明虽然对称,但是对称后面的值和当前的字符值不相等,所以继续递推         while( Pattern[i] != Pattern[k]  &&  k!=0 )             k=prefix[k-1];     //继续递归         if( Pattern[i] == Pattern[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++              prefix[i]=k+1;         else              prefix[i]=0;       //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0     }}*/void getnext(char *s,int *n){    int len = strlen(s),a = 0;    n[0] = len;    while(a<len-1 && s[a] == s[a+1])        a++;    n[1] = a;    a = 1;    for(int k = 2; k<len; k++)    {        int p = a+n[a]-1,l = n[k-a];        if((k-1)+l>=p)        {            int j = (p-k+1)>0?(p-k+1):0;            while(k+j<len && s[k+j] == s[j])                j++;            n[k] = j;            a = k;        }        else            n[k] = l;    }}int main(){    int n;    cin >> n;    getchar();    for(int m = 1; m<=n; m++)    {        gets(str);        int len = strlen(str);        for(int j = 0; j<len; j++)        {            str[len+j] = str[j];        }        str[2*len] = '\0';        getnext(str,next);        int l = 0,e = 0,g = 0;        makeNext();     // SetPrefix(str,nnext);        int k=0,p;          p=len-nnext[len-1];        if(len%p==0)        {           // printf("if\n");            k=p;        }        else        {//printf("else\n");            k=len;        }        for(int i = 0; i<k; i++)        {            if(next[i] >= len)                e++;            else if(next[i] > 0 && next[i]<len)            {                if(str[i+next[i]]>str[next[i]])                    g++;                else                    l++;            }            else if(next[i] == 0)            {                if(str[i]>str[0])                    g++;                else                    l++;            }        }        printf("Case %d: %d %d %d\n",m,l,e,g);    }    return 0;}

然后是未超时的代码,第一个是0的:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int MAXN = 10^100000*2;char str[MAXN];int next[MAXN];int nnext[MAXN];/*void makeNext(){    int q,i;    nnext[0]=0;    for(q=0,i=1;i<strlen(str);++i)    {        while(q>0 && str[i]!=str[q])        {            q=nnext[q-1];        }        if(str[i]==str[q])        {            ++q;        }        nnext[i]=q;    }}*/void SetPrefix(const char *Pattern, int prefix[]){     int len=strlen(Pattern);//模式字符串长度。     prefix[0]=0;     for(int i=1; i<len; i++)     {         int k=prefix[i-1];         //不断递归判断是否存在子对称,k=0说明不再有子对称,Pattern[i] != Pattern[k]说明虽然对称,但是对称后面的值和当前的字符值不相等,所以继续递推         while( Pattern[i] != Pattern[k]  &&  k!=0 )             k=prefix[k-1];     //继续递归         if( Pattern[i] == Pattern[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++              prefix[i]=k+1;         else              prefix[i]=0;       //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0     }}void getnext(char *s,int *n){    int len = strlen(s),a = 0;    n[0] = len;    while(a<len-1 && s[a] == s[a+1])        a++;    n[1] = a;    a = 1;    for(int k = 2; k<len; k++)    {        int p = a+n[a]-1,l = n[k-a];        if((k-1)+l>=p)        {            int j = (p-k+1)>0?(p-k+1):0;            while(k+j<len && s[k+j] == s[j])                j++;            n[k] = j;            a = k;        }        else            n[k] = l;    }}int main(){    int n;    cin >> n;    getchar();    for(int m = 1; m<=n; m++)    {        gets(str);        int len = strlen(str);        for(int j = 0; j<len; j++)        {            str[len+j] = str[j];        }        str[2*len] = '\0';        getnext(str,next);        int l = 0,e = 0,g = 0;      //  makeNext();      SetPrefix(str,nnext);        int k=0,p;          p=len-nnext[len-1];        if(len%p==0)        {           // printf("if\n");            k=p;        }        else        {//printf("else\n");            k=len;        }        for(int i = 0; i<k; i++)        {            if(next[i] >= len)                e++;            else if(next[i] > 0 && next[i]<len)            {                if(str[i+next[i]]>str[next[i]])                    g++;                else                    l++;            }            else if(next[i] == 0)            {                if(str[i]>str[0])                    g++;                else                    l++;            }        }        printf("Case %d: %d %d %d\n",m,l,e,g);    }    return 0;}





0 0
原创粉丝点击