HDU 4333(Revolving Digits)

题意:给定一个数字,每次可以把最后一位移到第一位,分别统计所有移位结果中比原数小,相等,大的数目,重复的数字只算一次。

 

分析:e-kmp,设原数字字符串为 b,长度为m,把原数字字符串复制两遍当作母串 a,原数字字符串当作模式串做e-kmp,则extend数组分两种情况:

①extend[i]==m,即以 a[i] 为开头的m个数字与 b 相等,即这个移位结果与原串相等;

②extend[i]<m,这种情况下的移位结果肯定和原串不等,因为前 extend[i] 个数字相等,所以只要比较一下第 extend[i]+1 个数字就i可以了;

综上差不多可以构建出思路了,不过题目要求重复出现的只算作一次,所以我们得用 kmp 算出 b 的最小循环节 k,若 b 是由若干个最小循环节 k 组成(末尾没有不完整的循环节),那这种字符串移位的所有结果只会出现 len(k) 个;若非这种情况,那么字符串的每一个移位结果都肯定不同。

 

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int N = 1e5*2+10;

char a[N],b[N];
int nxt[N],extend[N],nt[N];
int n,m;


int init(char s[],int len,int nxt[])
{
    nxt[0]=-1;
    int j=0,k=-1;
    while(j<len)
    {
        if(k==-1||s[j]==s[k])
        {
            ++j;++k; nxt[j]=k;
        }
        else
        {
            k=nxt[k];
        }
    }
    int cnt=len-nxt[len];
    if(len%cnt==0) return cnt;   //若干个完整相同循环节组成
    return len;
}

void GetNxt(char s[],int len,int nxt[])
{
    int i=0,j,po;
    nxt[0]=len;
    while(s[i]==s[i+1]&&i+1<len) i++; nxt[1]=i;   //计算nxt[1]
    po=1;    //初始化po的位置
    for(i=2;i<len;i++)
    {
        if(nxt[i-po]+i<nxt[po]+po) nxt[i]=nxt[i-po];  //第一种情况,可以直接得到nxt[i]的值
        else  //第二种情况,要继续匹配才能得到nxt[i]的值
        {
            j=nxt[po]+po-i;
            if(j<0) j=0;   //如果i>po+nxt[po],则要从头开始匹配
            while(i+j<len&&s[j]==s[j+i]) j++; nxt[i]=j;
            po=i;   //更新po的位置
        }
    }
}

//计算Extendend数组
void ExKMP(char a[],int lena,char b[],int lenb)
{
    int i=0,j,po;
    while(a[i]==b[i]&&i<lena&&i<lenb) i++; extend[0]=i;
    po=0;
    for(i=1;i<lena;i++)
    {
        if(nxt[i-po]+i<extend[po]+po)  //第一种情况,直接可以得到Extendend[i]的值
            extend[i]=nxt[i-po];
        else    //第二种情况,要继续匹配才能得到Extendend[i]的值
        {
            j=extend[po]+po-i;
            if(j<0) j=0;    //如果i>Extendend[po]+po则要从头开始匹配
            while(i+j<lena&&j<lenb&&a[j+i]==b[j]) j++; extend[i]=j;
            po=i;   //更新po的位置
        }
    }
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",b);
        m=strlen(b);
        for(int i=0;i<m;i++) a[i]=a[i+m]=b[i];
        n=m+m;
        GetNxt(b,m,nxt);
        ExKMP(a,n,b,m);
        int les=0,equ=0,mre=0;
        int k=init(b,m,nt);
        for(int i=0;i<k;i++)
        {
            if(extend[i]==m) {equ++;continue;}
            char s=a[i+extend[i]];
            char t=b[extend[i]];
            if(s<t) les++;
            else mre++;
        }
        printf("Case %d: %d %d %d\n",++cas,les,equ,mre);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值