您的位置:首页 > 其它

KMA & ex_KMP---Revolving Digits

2016-04-03 17:19 295 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: