String Problem (最小表示法+KMP)

113 篇文章 1 订阅

第一次遇到最小表示法,本来是吧所有的情况遍历了一下,结果超时了,看完题解发现是最小表示法,很经典。

【最小表示法】
先给代码,如果看懂了,就不用看下面的解释了。
最小表示法不仅可以求解字典序最小的,还可以求解字典序最大的。

       int i = 0, j = 1, k = 0, t;
        while(i < l && j < l && k < l)
        {
            int a=(i+k)%l;
            int b=(j+k)%l;
            t =str[a] - str[b];
            if(!t) k++;
            else
            {
                if(t > 0) i = i + k + 1;
                else j = j + k + 1;
                if(i == j) ++ j;
                k = 0;
            }
        }
        if(i>j) i=j;
        ++i;

例如:str=“SKYLONG”字符串;
向右移动
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
有7种情况,让你求出字典序最小的序列,用最小表示法,就最简单了。

两个字符串比较,如果开头有小的字符,那么那个字符串的字典序就一定是最小的,所以就可以将相同的跳过去,先有这个思想。

i代表的就是字符串的开头,j代表的就是与之相比的字符串的开头。
k代表两字符串匹配的长度。

int a=(i+k)%l;//避免i和ja超出l的范围,毕竟要遍历所有的情况
int b=(j+k)%l;
t =str[a] - str[b];//t的值代表str[a]和str[b]的大小。
if(!t) k++;//如果相等就比较下一个,毕竟k代表他们比较的长度。
else
{
    //因为我们要找字典序最小的
    //t>0是代表i代示的字符串较大,就找下一个字符串进行比较
    //t<0是代表j代示的字符串较大,就找下一个字符串进行比较
    if(t > 0) i = i + k + 1;
    else j = j + k + 1;
    if(i == j) ++ j;//如果i和j相等了,就将他俩错位一下,因为i一直代表的就是字典序最小的那个串,所以让j加一。
    k = 0;//然后让k为0,从头比较。
}
//跳出循环后
   if(i>j) i=j;//在执行这一步时if(t > 0) i = i + k + 1;说明j的字符串是字典序最小的。
   //如果发现已经跳出了循环,就可能造成j的是最小的,所以将i=j;
   i++;

如果理解了最小表示法求解字典序最小的,那么你就可以写出用最小表示法求解字典序最大的。

下面是这道题的代码:

题目链接:传送门
思路: 不管是字典序最小还是字典序最大,他们的个数是一样的,用kmp来求解他们的个数。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e6+9;
char str[maxn];
int nex[maxn];
void get_next(int n)
{
    int i=0,j=-1;
    nex[0]=-1;
    while(i<n)
    {
        if(j==-1||str[i]==str[j])
        {
            i++,j++;
            nex[i]=j;
        }
        else j=nex[j];
    }
}
int main()
{
    while(~scanf("%s",str))
    {
        memset(nex,0,sizeof(nex));
        int l=strlen(str);
        int i = 0, j = 1, k = 0, t;
        while(i < l && j < l && k < l)//找字典序最小的串的开头
        {
            int a=(i+k)%l;
            int b=(j+k)%l;
            t =str[a] - str[b];
            if(!t) k++;
            else
            {
                if(t > 0) i = i + k + 1;
                else j = j + k + 1;
                if(i == j) ++ j;
                k = 0;
            }
        }
        if(i>j) i=j;
        int min_=++i;
        i = 0, j = 1, k = 0, t;
        while(i < l && j < l && k < l)//找字典序最大的串的开头
        {
            int a=(i+k)%l;
            int b=(j+k)%l;
            t =str[a] - str[b];
            if(!t) k++;
            else
            {
                if(t <0) i = i + k + 1;
                else j = j + k + 1;
                if(i==j) ++ j;
                k = 0;
            }
        }
        if(i>j) i=j;
        int max_=++i;
        get_next(l);//kmp
        int h=l-nex[l];
        int jj=l/h;
        printf("%d %d %d %d\n",min_,jj,max_,jj);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值