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)

主要思想就是求i时记录p=max{a+extend[a]-1},1<=a<i 也就是当前最远匹配到哪里,这么做是为了以后直接利用next的信息

S[a,a+extend[a]-1]--->T[1,extend[a]-1]是一样的

那么从i开始的后缀的前一部分是和T[i-a+1..]相同的,就不用比较了因为已经有L=next[i-a+1]了

如果i+L-1<p,extend[i]=L

否则从s[p+1]和t[p-i+1+1]开始暴力往后匹配,并更新a

注意:本题要求不相同的数字,出现相同数字说明有循环节,KMP求一下看看有没有可以整除的循环节

#include <cstdio>
#include <cstring>
using namespace std;
const int N=2e6+5;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘; c=getchar();}
    return x*f;
}
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
int n,m;
int next[N],extend[N];
char s[N],t[N];
void getNext(char s[],int n){
    //printf("getNext %d\n",n);
    //for(int i=1;i<=n;i++) printf("%c",s[i]);

    next[1]=n;
    int a=1;
    while(a<n&&s[a]==s[a+1]) a++;
    next[2]=a-1;
    a=2;
    for(int i=3;i<=n;i++){
        int p=a+next[a]-1,L=next[i-a+1];
        if(i+L-1<p) next[i]=L;
        else{
            int j=max(p-i+1,0);//!!
            while(i+j<=n&&s[i+j]==s[j+1]) j++;
            next[i]=j;a=i;
        }
    }
    //for(int i=1;i<=n;i++) printf("next %d %d\n",i,next[i]);
}
void getExtend(char s[],char t[]){
    //printf("getExtend %d\n",n);
    //for(int i=1;i<=n;i++) printf("%c",s[i]);puts("");
    getNext(t,m);
    int mn=min(n,m),a=1;
    while(a<=mn&&s[a]==t[a]) a++;
    extend[1]=a-1;
    a=1;
    for(int i=2;i<=n;i++){
        int p=a+extend[a]-1,L=next[i-a+1];
        if(i+L-1<p) extend[i]=L;
        else{
            int j=max(p-i+1,0);
            while(i+j<=n&&s[i+j]==t[j+1]) j++;
            extend[i]=j;
            a=i;
        }
    }
    //for(int i=1;i<=n;i++) printf("extend %d %d\n",i,extend[i]);
}
int fail[N];
void getFail(char s[],int n){
    fail[1]=0;
    for(int i=2;i<=n;i++){
        int j=fail[i-1];
        while(j&&s[j+1]!=s[i]) j=fail[j];
        fail[i]=s[j+1]==s[i]?j+1:0;
    }
}
int L,E,G;
void solve(){
    getExtend(s,t);
    L=E=G=0;
    for(int i=1;i<=m;i++){
        int lcp=extend[i];
        if(lcp>=m) E++;
        else if(t[1+lcp]<s[i+lcp]) G++;
        else L++;
    }
//for(int i=1;i<=n;i++) printf("hi %d %d %d\n",i,next[i],extend[i]);
    getFail(t,m);
    int _=m%(m-fail[m])==0?m/(m-fail[m]):1;
    L/=_;E/=_;G/=_;
}

int main(){
    freopen("in","r",stdin);
    int T=read(),cas=0;
    while(T--){
        scanf("%s",s+1);
        n=m=strlen(s+1);
        for(int i=1;i<=n;i++) s[i+n]=t[i]=s[i];
        n<<=1;
        solve();
        printf("Case %d: %d %d %d\n",++cas,L,E,G);
    }
}
时间: 2024-10-23 15:11:46

HDU 4333 Revolving Digits [扩展KMP]【学习笔记】的相关文章

HDU 4333 Revolving Digits 扩展KMP

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

扩展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)

扩展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

http://acm.hdu.edu.cn/showproblem.php?pid=4333 [题意] 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的数比原数小的个数,相等的个数,大的个数 [思路] 这个数很大,用字符串处理 比较两个字符串的大小,一位一位很耗时,可以求出最长公共前缀,只比较最长公共前缀后一位 每次将数的最后一位放到最后一位,如abcd变成dabc,cdab,bcda,相当于abcdabcd各个后缀的前四位 这样就变成了求abcdabc

字符串(扩展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

HDU - 4333 Revolving Digits(拓展kmp+最小循环节)

1.给一个数字字符串s,可以把它的最后一个字符放到最前面变为另一个数字,直到又变为原来的s.求这个过程中比原来的数字小的.相等的.大的数字各有多少. 例如:字符串123,变换过程:123 -> 312 -> 231 -> 123 因为:312>123, 231>123, 123=123 所以答案是:0 1 2 2.令str1=s,str2=s+s,然后str1作为子串,str2作为主串,进行扩展kmp求出str2[i...len2-1]与str1[0...len1-1]的最长

hdu 4333 Revolving Digits

Revolving Digits http://acm.hdu.edu.cn/showproblem.php?pid=4333 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description One day Silence is interested in revolving the digits of a positive integer. In th

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),它的最后一个数字放到最前面形成一个新的数字,然后对新的数字继续处理. 问有多少种不同的

【HDOJ】4333 Revolving Digits

扩展KMP基础题目. 1 /* 4333 */ 2 #include <iostream> 3 #include <sstream> 4 #include <string> 5 #include <map> 6 #include <queue> 7 #include <set> 8 #include <stack> 9 #include <vector> 10 #include <deque> 11