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

题目链接:传送门

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
  
  
abcder aaaaaa ababab
 

Sample Output
  
  
1 1 6 1 1 6 1 6 1 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;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值