hdu4333 Revolving Digits 扩展kmp

/**
参考:http://blog.csdn.net/acdreamers/article/details/8313828
参考:http://www.61mon.com/index.php/archives/186/

题目:hdu4333 Revolving Digits
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333
题意:
给定一个数字(不含前导0),它的最后一个数字放到最前面形成一个新的数字,然后对新的数字继续处理。
问有多少种不同的数字并原数字小,等于,大于。(注意是不同的数字,注意不是字典序大小,就是比数字大小)
如果新数字有前导0,那么去掉0.显然该数字大小小于原数字。
思路:
在原数字后面连接原数字得到串t。那么i在[0,m-1],从i开始长度为m的数字就是可以转化的数字。 如果原数字有循环节,那么
只需要计算一个最小循环节长度的结果。(通过kmp找一个字符串的最小循环节。)

定义extend[i]表示t串从i开始的和原数字的最长公共前缀。
[i,i+m-1]和原数字的比较:
如果t[i]==‘0‘; 那么小于。
如果extend[i]>=m;那么等于。
如果extend[i]<m;如果t[i+extend[i]]>p[extend[i]] 那么大于。
否则小于。

Next[i]: T[i]...T[m - 1]与 T 的最长相同前缀长度;

extend[i]: S[i]...S[n - 1]与 T 的最长相同前缀长度。

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<set>
#include <iostream>
#include <vector>
using namespace std;
const int N = 2e5+10000;
int extend[N*2], f[N], Next[N];///不可以用next[N],会编译错误。
char t[N*2], p[N];
void GetNext(char *T,int* next)
{
    int a=0;
    int Tlen=strlen(T);
    next[0]=Tlen;
    while(a<Tlen-1&&T[a]==T[a+1]) a++;
    next[1]=a;
    a=1;
    for(int k=2;k<Tlen;k++)
    {
        int p=a+next[a]-1,L=next[k-a];
        if((k-1)+L>=p)
        {
            int j=(p-k+1)>0? p-k+1:0;
            while(k+j<Tlen&&T[k+j]==T[j]) j++;
            next[k]=j;
            a=k;
        }
        else next[k]=L;
    }
}

void GetExtend(char *S,char *T,int* next,int* extend)
{
    int a=0;
    GetNext(T,next);
    int Slen=strlen(S);
    int Tlen=strlen(T);
    int MinLen=Slen<Tlen? Slen:Tlen;
    while(a<MinLen&&S[a]==T[a]) a++;
    extend[0]=a;
    a=0;
    for(int k=1;k<Slen;k++)
    {
        int p=a+extend[a]-1,L=next[k-a];
        if((k-1)+L>=p)
        {
            int j=(p-k+1)>0? p-k+1:0;
            while(k+j<Slen&&j<Tlen&&S[k+j]==T[j]) j++;
            extend[k]=j;
            a=k;
        }
        else extend[k]=L;
    }
}

void getFail(char* P,int* f)
{
    int m = strlen(P);
    f[0] = f[1] = 0;
    for(int i = 1; i < m; i++){
        int j = f[i];
        while(j&&P[i]!=P[j]) j = f[j];
        f[i+1] = (P[i]==P[j])?j+1:0;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    int cas = 1;
    cin>>T;
    while(T--){
        scanf("%s",p);
        memset(extend, 0, sizeof extend);
        int m = strlen(p);
        for(int i = 0; i < m*2; i++){
            t[i] = p[i%m];
        }
        t[2*m] = ‘\0‘;
        GetExtend(t,p,Next,extend);
        getFail(p,f);
        int stop = m;
        if(f[m]!=0&&m%(m-f[m])==0){
            stop = m-f[m];
        }
        int equ, big, small;
        equ = big = small = 0;
        for(int i = 0; i < m&&i<stop; i++){
            if(t[i]==‘0‘){
                small++; continue;
            }
            if(extend[i]>=m){
                equ++;
            }else
            {
                if(t[i+extend[i]]>p[extend[i]]) big++;
                else small++;
            }
        }
        printf("Case %d: %d %d %d\n",cas++,small,equ,big);
    }
    return 0;
}
时间: 2024-10-24 20:37:54

hdu4333 Revolving Digits 扩展kmp的相关文章

HDU 4333 Revolving Digits 扩展KMP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333 题意:给以数字字符串,移动最后若干位到最前边,统计得到的数字有多少比原来大,有多少和原来相同,有多少比原来的小. 思路:拓展KMP中的next数组标记的是子串和母串的公共前缀的长度,要将字符串长度变成原来二倍,这样如果变换后不是完全相同的数字也即公共前缀长度大于等于字符串长度,那么字母串公共前缀的下一位的大小比较就是题目所要求的比较.由于相同的数字串只算一次,则只要统计比较第一个"循环节"

HDU 4333 Revolving Digits [扩展KMP]【学习笔记】

题意:给一个数字,每一次把它的最后一位拿到最前面,一直那样下去,分别求形成的数字小于,等于和大于原来数的个数. SAM乱搞失败 当然要先变SS了 然后考虑每个后缀前长为n个字符,把它跟S比较就行了 如果用后缀家族的话复杂度要加上log,本题会TLE吧 求一个串S的每个后缀与另一个串T的最长公共前缀可以用扩展KMP!复杂度O(n+m) 看课件吧 从1开始写真不容易以后再也不从1开始了,判断位置好麻烦好容易错 next[i]=LCP(T[i,m],T) extend[i]=LCP(S[i,n],T)

hdu4333 Revolving Digits(扩展kmp+kmp最小循环节)

题目链接:点击打开链接 题意描述:给定一个字符串,可以把字符串的后x位移到字符串前面组成一个新的字符串?问对于所有的新组成的字符串中去掉重复的之后,比原串小的个数,等于的个数,大于的个数? 解题思路:扩展KMP(next1[i]数组含义:x[i···len-1]与x[0···len-1]的最长公共前缀) 分析:首先我们把字符串s复制一遍接到原字符串后面形成ss,这样在ss中以i(i>=0&&i<len)为起点的长度为len的字符串就是所有可能的新字符串: 讲到这里,问题转变为在

[xjoi1898] [hdu4333]Revolving Digits

/*注意注意:本题非hdu4333原题,而是简化版,原版有多组数据.但此代码在修改输入后依旧可以通过多组数据*/ 给出一个数字串,问有多少本质不同同构串比原串小,一样,大.同构串是指,对于原串S[1-N]通过旋转变成同构串S[i-N]+S[0-i-1].本质不同,指的是字符串不一样. 输入格式: 一行一个数字串 输出格式: 一行,输出本质不同的同构串比原串小,一样,大的三个数,用一个空格分隔开. 样例输入: 123123 样例输出: 0 1 2 数据范围: 数字串长度<=100000 首先,我们

Part.5【马拉车&amp;扩展KMP】

Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n).网上对其介绍的资料已经挺多了的,请善用搜索引擎. 而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀[KMP更像是一个自动机] 题目: POJ 1159: Palindrome 求原字符串最少增加几个字符后可变成回文串,相当于求最长回文子序列的长度. 解法:直接求串S和反转串Sr的最长公共子序列. #include <cstdlib> #include <cstdio> #include <

扩展KMP - HDU 4333 Revolving Digits

Revolving Digits Problem's Link: http://acm.hdu.edu.cn/showproblem.php?pid=4333 Mean: 给你一个字符串,你可以将该字符串的任意长度后缀截取下来然后接到最前面,让你统计所有新串中有多少种字典序小于.等于.大于原串. analyse: KMP的经典题. 首先我们将原串扩展成两倍,算一遍扩展KMP(自匹配),时间复杂度O(n). 这样一来,我们就得到了eKMP[i],eKMP[i]代表s[i...len-1]与s的最长

字符串(扩展KMP):HDU 4333 Revolving Digits

Revolving Digits Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 24729    Accepted Submission(s): 5381 Problem Description One day Silence is interested in revolving the digits of a positive int

扩展KMP,附上例题(HDU - 4333 Revolving Digits)

给出模板串S和串T,长度分别为Slen和Tlen,在线性时间内,对于每个S[i](0<=i<Slen),求出S[i..Slen-1]与T的 最长公共前缀长度,记为extend[i],extend[i]存放s[i]开始与T的最长公共前缀长度. 例子 a a a a a a a b b b a a a a a c extend 5 4 3 2 1 0 0 0 0 0 HDU - 4333 Revolving Digits Time Limit: 3000/1000 MS (Java/Others)

Revolving Digits(扩展Kmp+最小循环节)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4333 Revolving Digits Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 28267    Accepted Submission(s): 6363 Problem Description One day Silence is