HDU 4333 Revolving Digits(KMP:循环节+扩展KMP)

来源:互联网 发布:淘宝首页的广告费 编辑:程序博客网 时间:2024/05/16 15:07

HDU 4333 Revolving Digits(KMP:循环节+扩展KMP)

http://acm.hdu.edu.cn/showproblem.php?pid=4333

题意:给你一个n位的正整数X(<=10^100000),然后你依次将该整数的后1,2,3…,n位放到前面去,形成了一个新整数.问你这n个新整数中,有多少个比X小,与X相等,比X大?(新整数与原先的X均无前导0)

分析:

       先用扩展KMP求出A[i]数组,A[i]数组表示以第i位为首部的前缀与串T的前缀最长公共部分.

       假设我们当前要把从i到n-1的串移到串头去,那么此时A[i]=3,我们应该用A[i+3]与A[3]比较,就知道新串与原始串的大小关系.(想想是不是)

       但是要注意如果A[i]+i==n(n为原始串的长度)的话,就不好判断了,因为你不知道超出串长还有多少位与原始串前缀相等(这里需要循环移位继续判断了).所以我们这里将原始串T变成串TT,即把原始串翻倍即可.然后用扩展KMP处理翻倍了的原始串.

然后对于位置[0,n-1]考虑其A[i]的值,如果A[i]+i>=n(n依然为T的长度)表示把[i,n-1]的串放到前面形成的新串与T正好相等.(想想是不是)

如果A[i]>0且A[i]+i<n,那么只需要比较TT[A[i]+i] 与TT[A[i]-1]的大小即可.

如果A[i]==0,那么首先看TT[i]是不是等于0,如果TT[i]!=0,那么直接比较TT[i]与TT[0].否则如果TT[i]==0,那么由于新数有前导0,所以新数的位数肯定少,所以新数必然变小,所以该情况也是直接比较TT[i]与TT[0]即可.

注意:如果两个不同的移动操作产生了同一个数的话,就只能算一次.通过测试可以发现只有串T是正好具有k(k>=2)个循环节的时候,才会出现重复串.否则不可能重复.

且当串T具有k个最短循环节的时候,每个新串被重复了K次.

AC代码:

#include <iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const int MAXN=100000+1000;char T[MAXN*2];int A[MAXN*2],next[MAXN];int n;//原始串T的长度void EKMP(){    int j=0;    A[0]=n;    while(1+j<n*2 && T[0+j]==T[1+j])        j++;    A[1]=j;    int k=1;    for(int i=2;i<n;i++)    {        int len=k+A[k]-1,L=A[i-k];        if(L<len-i+1)            A[i]=L;        else        {            j=max(0,len-i+1);            while(i+j<2*n && T[0+j]==T[i+j])                j++;            A[i]=j;            k=i;        }    }}void getFail(){    next[0]=next[1]=0;    for(int i=1;i<n;i++)    {        int j=next[i];        while(j && T[i]!=T[j]) j=next[j];        next[i+1]=(T[i]==T[j])?j+1:0;    }}int main(){    int K;    scanf("%d",&K);    for(int kase=1;kase<=K;kase++)    {        scanf("%s",T);        n=strlen(T);        getFail();        for(int i=n;i<2*n;i++)//将串T变成串TT            T[i]=T[i-n];        T[2*n]=0;        EKMP();        int L=0,E=0,G=0;        for(int i=0;i<n;i++)        {            if(A[i]>=n)                E++;            else//0<=A[i]<n            {                if(T[i+A[i]]>T[A[i]]) G++;                else L++;            }        }        if(next[n]>0 && n%(n-next[n])==0)        {            L/=n/(n-next[n]);            E/=n/(n-next[n]);            G/=n/(n-next[n]);        }        printf("Case %d: %d %d %d\n",kase,L,E,G);    }    return 0;}


0 0
原创粉丝点击