KMA & ex_KMP---Revolving Digits

HDU  4333

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
 

题意:给一个数,从这个数的尾端取一个数字放在前段,循环进行,输出有多少得到的数大于原来的数、等于原来的数、小于原来的数,注意相同的数只计算一次。‘

思路:使用ex_KMP算法可以得到从第i个字符开始和前缀匹配的最大长度,所以和原来的数进行比较大小时,可以减少比较次数节省时间。由于相同的数只能计算一次,所以可以使用KMP算法找出最小周期,对最小周期的字符进行计算。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
int nex[100005],nex2[100005];
int a,b,c;
char str[100005];

void ex_next(int length)
{
    ///nex[i]: 以第i位置开始的子串与T的前缀的最大长度;
    int i;
    nex[0]=length;
    for(i=0; i<length-1&&str[i]==str[i+1]; i++); ///前缀都是同一个字母的时候;
    nex[1]=i;
    int a=1;///a为使匹配到最远的地方时的起始匹配地点;
    for(int k=2; k<length; k++)
    {
        int p=a+nex[a]-1,L=nex[k-a];
        if( (k-1)+L>=p )
        {
            int j=(p-k+1)>0?(p-k+1):0;
            while(k+j<length&&str[k+j]==str[j]) j++;
            /// 枚举(p+1,length) 与(p-k+1,length) 区间比较;
            nex[k]=j,a=k;
        }
        else nex[k]=L;
    }
}

void next_(int len)
{
    int k=0;
    nex2[0]=0;
    for(int i=1; i<len; i++)
    {
        while(k>0&&str[k]!=str[i])
            k=nex2[k-1];
        if(str[k]==str[i])
            k++;
        nex2[i]=k;
    }
}

void solve(int len)
{
    int t,s;
    for(int i=1; i<len; i++)
    {
        t=nex[i];
        if(i+t<len)
        {
            if(str[t+i]>str[t]) c++;
            else   a++;
        }
        else
        {
            s=nex[t];
            if(s<i)
            {
                if(str[t+s]<str[s]) c++;
                else  a++;
            }
        }
    }
}

int main()
{
    int T,Case=1;
    scanf("%d",&T);
    while(T--)
    {
        a=0;
        b=1;
        c=0;
        scanf("%s",str);
        int len=strlen(str);
        next_(len);
        int minn=len-nex2[len-1];
        if(len%minn==0) str[minn]='\0';
        else minn=len;
        //cout<<minn<<endl;
        ex_next(minn);
        solve(minn);
        printf("Case %d: %d %d %d\n",Case++,a,b,c);
    }
    return 0;
}

 

posted @ 2016-04-03 17:19  茶飘香~  阅读(180)  评论(0编辑  收藏  举报