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

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


你可能感兴趣的:(ACM)