KMP算法

2021.12.16 by Ludwig

最近重新学习kmp算法,好像在大学乃至考研我都没闹明白kmp是咋回事,更不用说手撕,但也总觉得这要逃下去不是办法,花了两天时间,总算略微懂了点皮毛。网上的教程五花八门,next数组也各式各样。这篇不是教程也不是笔记,仅作为我个人的学习记录。

这篇博客代码和最后的图解使我对kmp的理解有很多帮助:https://www.zhihu.com/question/21923021 ,仅作为分享,此外还参考了王道书,学习了怎么手算nextval数组做考研题目,怎么用代码求解nextval数组。

最后运用kmp算法,完成了leetcode.28

学习kmp意外的收获是,对参数传递一个数组,对指针的使用有了更深刻的理解,运用也更自如。

在期间踩了很重要的一个关于c++语法的坑,出现了一个使我百思不得其解的bug,以下为记录。

next数组

求解next数组,现在在我的理解下就是模式串先自身掐头去尾作匹配,next[0]恒为-1,代码如下:

void getNext(int* next,string s){
      next[0]=-1;
      int i=0;
      int j=-1;//想象两个模式串,串首差一位错开作模式匹配,以此得到最长相等前后缀大小
      while(i<s.size()){
          if(j==-1||s[i]==s[j]){
              i++;
              j++;
              next[i]=j;//理解一下为什么j=-1时是怎样操作的
              //如果j=-1,说明第一个位置没有匹配上,此时,i后移,由于++,j又变为0
          }else{
              j=next[j];//回退
          }
      }  
}

nextval数组

nextval数组是对next数组的优化。改动非常的小,但却很重要。

void getNextVal(int* nextval,string s){
      nextval[0]=-1;
      int i=0;
      int j=-1;
      while(i<s.size()){
          if(j==-1||s[i]==s[j]){
              i++;
              j++;
              /****************************/
              if(s[i]==s[j]){
                  nextval[i]=nextval[j];
            }else{
                  nextval[i]=j;
            }
              /****************************/
        }else{
              j=nextval[j];
        }
    }
}

leetcode.28 实现 strStr()

class Solution {
public:
    void getNext(int* next,string s){
        next[0]=-1;
        int i=0;
        int j=-1;
        while(i<s.size()){
            if(j==-1||s[i]==s[j]){
                i++;
                j++;
                next[i]=j;
            }else{
                j=next[j];
            }
        }
    }
    int strStr(string haystack, string needle) {
        int next[needle.size()+1];
        int i=0;
        int j=0;
        getNext(next,needle);
        //注意这里while内不能写成`j<needle.size()`
        //因为j可能等于-1,见下文c++的坑
        int nSize=needle.size();
        while(i<haystack.size()&&j<nSize){
            if(j==-1||haystack[i]==needle[j]){
                i++;
                j++;
            }else{
                j=next[j];
            }
        }
        if(j==nSize){
            return i-j;
        }else{
            return -1;
        }
    }
};

note: 主串指针i永远不回溯。

c++的坑

#include <string>
#include <iostream>
using namespace std;

int main(){
      string a="hello";
      cout<<"a.size()="<<a.size()<<endl;
      if((-1)<a.size()){
                cout<<"true"<<endl;
    }else{
          cout<<"false"<<endl;
    }
    return 0;
}
jaden@sunjuanxiongdeMacBook-Pro ~ %g++ test.cpp 
jaden@sunjuanxiongdeMacBook-Pro ~ %./a.out 
a.size()=5
false
jaden@sunjuanxiongdeMacBook-Pro ~ %

为什么会这样,-1明明小于a.size(),为什么打印结果是false??

在c++中,诸如string、vector等类型的size()方法返回的是一个size_type类型的无符号整型!,如果在表达式中混和使用了带符号数和无符号数进行比较,将产生意想不到的结果。运算时-1会被当成无符号整型数处理,此时-1的二进制最高位为1是一个非常大的数,因此返回false。在进行判断size()时,还是应该使用0进行比较。或者用一个int型变量记录size()的值。

此处还可以写成 j+1<needle.size()+1, 这样也能规避错误。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值