HDU 3374 KMP-最小最大表示法的原理详解

来源:互联网 发布:学生基本护肤步骤知乎 编辑:程序博客网 时间:2024/06/07 10:26

题目链接:传送门

String Problem

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3157    Accepted Submission(s): 1275


Problem Description
Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
  Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
 

Input
  Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters.
 

Output
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
 

Sample Input
abcderaaaaaaababab
 

Sample Output
1 1 6 11 6 1 61 3 2 3
题意:在这些可以得到的字符串中,字典序最大的字符串在第几个出现,出现了几次。最小的字符串亦然。

思路:刚学会这个代码,重点是代码有一个地方不好搞明白(一会儿重点解释代码的原理,自己想的原理,我也不知道对不对大笑)。

出现的次数应该都能想到,求出字符串的最小周期x是多少。

        int p=1;        if(ls%(ls-nex[ls])==0)//ls-nex[ls]为循环节。ls为串长度            p=ls/(ls-nex[ls]);
p即为次数。

怎么求最大(小)的子串排第几呢。模拟代码自己先理解,代码不算难理解。(后面我只会解释当时困惑我的地方)

int getminmax(int flag)  //求最小值时flag=0,最大值为1;{    int i=0,j=1,k=0;    while(i<ls&&j<ls&&k<ls)    {        int t=s[(i+k)%ls]-s[(j+k)%ls];        if(!t) k++;        else        {            if(flag)            {               if(t>0) j+=k+1;               else   i+=k+1;            }            else            {                if(t>0) i+=k+1;                else   j+=k+1;            }            if(i==j) j++;             k=0;        }    }    return i>j?j:i;}

当时一直搞不懂为什么j+=k+1,i+=k+1。为什么i和j可以这样跳呢,请思考一下(它的原理):

若我现在要找最小串,两个比较的串的开头分别是i,j,串比较的长度是k+1( t ! =0 ),如果 t > 0  ( 串 i > 串 j )  请思考在这两个串( i 和 j )的前(k+1)位,你是否能在串i的前(k+1)位中找到一个子串,使得这个子串比串 j 的前几位还小,答案是肯定找不到的。(假设你找到了一个,也就是说存在一个串 i 的前k+1位的子串比 j 的前几位还小,那么你再退回去重新从i,j 的第一位开始比较,一定得到的是 t < 0 ( 这就与前边的t>0矛盾了))所以假设不成立,所以你在串i的前(k+1)位中找不到一个比 j 小的子串 ,那么你就能可以不用考虑串i的前k+1位了,所以直接跳到k+1位的下一位。

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N=1000005;char s[N];int ls,nex[N];void getnext(){    nex[0]=-1;    int x=-1,y=0;    while(y<ls)    {        while(x!=-1&&s[x]!=s[y])            x=nex[x];        nex[++y]=++x;    }    return ;}int getminmax(int flag)  //最小最大表示法0、1{    int i=0,j=1,k=0;    while(i<ls&&j<ls&&k<ls)    {        int t=s[(i+k)%ls]-s[(j+k)%ls];        if(!t) k++;        else        {            if(flag)            {               if(t>0) j+=k+1;               else   i+=k+1;            }            else            {                if(t>0) i+=k+1;                else   j+=k+1;            }            if(i==j) j++;             k=0;        }    }    return i>j?j:i;}int main(){    while(~scanf("%s",s))    {        ls=strlen(s);        getnext();        int p=1;        if(ls%(ls-nex[ls])==0)            p=ls/(ls-nex[ls]);        printf("%d %d %d %d\n",getminmax(0)+1,p,getminmax(1)+1,p);    }    return 0;}







原创粉丝点击