HDU 4333 Revolving Digits 扩展KMP

Revolving Digits Time Limit:1000MS    Memory Limit:32768KB    64bit IO Format:%I64d & %I64u


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

      
      
      
      
1 341
 

Sample Output

      
      
      
      
Case 1: 1 1 1
 
题意:给出一个数字,每一次将数字的最后一位拿到第一位得到一个新的数字,问有多少不相同的数字满足:大于原数,等于原数,小于原数的条件。

方法:扩展KMP

思路:这个数很明显要当作字符串来处理。我们在比较两个数字的时候,首先比较第一位,相同则比较下一位,以此类推,直到不同或者全部相同,然而这个比较过程会很漫长,如果有什么办法可以直接得到两个数的最长公共前缀,那就方便多了,直接比较下一位就好了,这里,就需要使用扩展KMP算法。


扩展KMP:两个串,s和T,计算s的所有后缀与T的最长公共字串的长度。


    将数的末端数字拿到首端也可以等价为将原串num复制一遍接到尾部,得到extnum,然后问题就很清楚了,只要让s=extnum,T=num,那么就可以求出所有的数字与原数字的最长公共前缀长度,然后再比较一下就可以了。

    另外,还需要解决重复的问题,这个需要用到循环节的概念,如果一个串经过上述操作会产生相同的数字,那就意味着这个串中含有整数个循环节,否则在拼接的时候肯定不会产生相同的数,比如121拼接完是121121,没有循环部分,而1212拼接完是12121212,很明显12这个开始循环了,因此如果原串中含有整数个循环节,那么只需要把求出来的值除以循环节的个数即可,因为每个循环节所产生的数字一定是一样的。

#include <iostream>
#include <string.h>
#include <stdio.h>
#define MAX 400010
using namespace std;
int g,e,l;
int Next[MAX*2];
char num[MAX],extnum[MAX*2];
void getNext(char *s,int *next)
{
    int nn = strlen(s);
    next[0] = nn;
    int p = 0;
    while (p+1 < nn && s[p] == s[p+1])
        p++;
    next[1] = p;
    int k = 1, L;
    for (int i = 2; i < nn; i++)
    {
        p =     k + next[k] - 1;
        L = next[i - k];
        if (i + L <= p) next[i] = L;
        else
        {
            int j = p - i + 1;
            if (j < 0) j = 0;
            while (i + j < nn && s[i + j] == s[j])
                j++;
            next[i] = j;
            k = i;
        }
    }
    /*    for (int i=0;i<nn;i++){
            cout<< next[i] <<" ";
        }cout<<endl;
    */
}

void getExtend(char *s,char *T,int *extend,int *next)
{
    int nn = strlen(s) ;
    getNext(s,next);
    int p = 0;
    while (p < nn && s[p] == T[p])
        p++;
    extend[0] = p;
    //extend[1] = p;
    int k = 0, L;
    for (int i = 1; i < nn; i++)
    {
        p =     k + extend[k] - 1;
        L = next[i - k];
        if (i + L <= p) extend[i] = L;
        else
        {
            int j = p - i + 1;
            if (j < 0) j = 0;
            while (i + j < nn && s[i + j] == T[j])
                j++;
            extend[i] = j;
            k = i;
        }
    }
}

void getres()
{
    int extlen=strlen(extnum);
    int len=extlen/2;
    for(int i=0;i<len;i++)
	{
		if(Next[i]>=len)
			e++;
		else
		{
			if(extnum[i+Next[i]]<num[Next[i]])
				l++;
			else
				g++;
		}
	}
}

int getFail(char *p)
{
	int f[MAX];
    int m=strlen(p);
    f[0]=f[1]=0;
    for(int i=1; i<m; ++i)
    {
        int j=f[i];
        while(j && p[i]!=p[j])j=f[j];
        f[i+1] = p[i]==p[j]?1+j:0;
    }
    int len=strlen(p);
    return len-f[len];
}

void output(int cas,int len,int loop)
{
	int tol=1;
	if(len%loop==0)
		tol=len/loop;
	l/=tol;
	g/=tol;
	e/=tol;
	printf("Case %d: %d %d %d\n",cas,l,e,g);
}


void show(int len)
{
	for(int i=0;i<len;i++)
	{
		printf("%d ",num[i]);
	}
	printf("\n");
}

void init()
{
    int len=strlen(num);
    //getNext(num,next);
    strcpy(extnum,num);
    for(int i=0; i<len; i++)
        extnum[i+len]=num[i];
	extnum[len*2]='\0';
	getNext(extnum,Next);
    //getExtend(extnum,extnum,ext,next);
}

int main()
{
    int t,cas,loop,len;
    scanf("%d",&t);
    cas=0;
    while(t--)
    {
        g=e=l=0;
        scanf("%s",num);
        len=strlen(num);
        loop=getFail(num);
        init();
        //show(next,strlen(extnum));
        getres();
        //printf("loop=%d\n",loop);
        output(++cas,len,loop);
    }
    return 0;
}








你可能感兴趣的:(HDU,digits,扩展kmp,4333,Revolving)