HDU 4333 Revolving Digits(kmp+扩展kmp)

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;
}

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