Description
给定一个数字,每一次将该数的第一位放到放到最后一位,求所有组成的不同的数比原数小的个数,相等的个数,大的个数
Input
第一行一整数T(T<=50)表示用例组数,每组用例占一行为一整数n(n<=10^100000)
Output
输出所有组成的不同的数中比原数小的个数,相等的个数,大的个数
Sample Input
1
341
Sample Output
Case 1: 1 1 1
Solution
因为只考虑不相同的数字,所以至多有len个不同的数(len为n的位数),将n看作一个字符串a,将a扩展两倍,那么这len个不同的数即为a的len个长度为len的子串,这len个数与原数的比较就变成了a的一个后缀的前缀与a的前缀的匹配,如果匹配长度大于等于len则两数相等,如果匹配长度小于len,则比较第一个失配的数字即可判断两数的大小关系,但这样做没有考虑着len个数的重复问题,例如111答案应该是0 1 0但这样做的答案是0 3 0,所以还需要去重,去重很简单,因为只有原串完全由最小循环节组成时才会重复,而且重复次数为len/l,l为最小循环节长度,而求最小循环节长度可以用kmp来解决,l=len-next[len]即为可能的最小循环节长度,判断len%l是否为0即可知道原串是否完全由最小循环节组成,那么重复次数t=len%l==0?len/l:1,最后将三个结果除以t即为去重后的正确答案
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 222222
char a[maxn];
int nex[maxn];
void kmp(char *a)
{
memset(nex,0,sizeof(nex));
int la=strlen(a);
for(int i=0,j=-1;i<=la;i++,j++)
{
nex[i]=j;
while(~j&&a[i]!=a[j])
j=nex[j];
}
}
void extend_kmp(char *a)
{
int i,j,k,la=strlen(a);
nex[0]=la;
for(i=0;i+1<la&&a[i+1]==a[i];i++);
nex[1]=i;
k=1;
for(i=2;i<la;i++)
{
int len=k+nex[k];
nex[i]=min(nex[i-k],max(0,len-i));
while(i+nex[i]<la&&a[nex[i]]==a[i+nex[i]])nex[i]++;
if(i+nex[i]>k+nex[k])k=i;
}
}
int main()
{
int T,res=1;
scanf("%d",&T);
while(T--)
{
scanf("%s",a);
int len=strlen(a);
kmp(a);
int t=len%(len-nex[len])==0?len/(len-nex[len]):1;
for(int i=len;i<2*len;i++)a[i]=a[i-len];
a[2*len]='\0';
extend_kmp(a);
int cnt1=0,cnt2=0,cnt3=0;
for(int i=0;i<len;i++)
{
if(nex[i]>=len)cnt2++;
else if(a[nex[i]]>a[i+nex[i]])cnt1++;
else cnt3++;
}
printf("Case %d: %d %d %d\n",res++,cnt1/t,cnt2/t,cnt3/t);
}
return 0;
}