代码随想录 Day19 字符串 | LC28 实现strStr() 【KMP经典题目】

六、实现strStr()

题目

力扣28:找出字符串中第一个匹配的下标

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

示例1:

输入:haystack = “sadbutsad”, needle = “sad”
输出:0
解释:“sad” 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

示例2:

输入:haystack = “leetcode”, needle = “leeto”
输出:-1
解释:“leeto” 没有在 “leetcode” 中出现,所以返回 -1 。

提示:

  • 1 <= haystack.length, needle.length <= 104
  • haystack 和 needle 仅由小写英文字符组成

解题思路分析:


本题是KMP 经典题目。

KMP算法理论基础:

KMP算法的名字由来:

因为是由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP。

KMP算法解决的问题:

解决字符串匹配的问题;在文本串种查找是否出现过模式串。

例如:

  • 文本串:aabaabaafa
  • 模式串:aabaaf

暴力匹配: 从下标0处开始,匹配失败后回到下标1处开始重新匹配;时间复杂度为O(m+n)

KMP: 出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。

在这里插入图片描述

前缀表:

KMP可以通过前缀表来做到跳到b直接去继续匹配;

前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配

前缀:不包含尾字母的所有子串;

  • a;aa;aab;aaba;aabaa

后缀:不包含首字母的所有子串;

  • f;af;aaf;baaf;abaaf

最长相等前后缀:

  • a 最长相等前后缀为0;
  • aa 最长相等前后缀为1;
  • aab 最长相等前后缀为0;
  • aaba 最长相等前后缀为1;
  • aabaa 最长相等前后缀为2;
  • aabaaf 最长相等前后缀为0;

前缀表如下图所示:
在这里插入图片描述

使用前缀表的匹配过程:

当指针移动到f时,发现字符串不匹配,那么就跳到最长相等前缀的后面,也就是跳到最后一个a对应的前缀表数字的位置,即跳到下标为2的位置继续匹配;也就是从b继续匹配。

因为前缀表中的2表示的就是相等前后缀的长度,要跳到前缀的后面,前缀的后面的下标正好就是前缀的长度2。

next数组:

next数组放的也是前缀表,但是可能会有一些调整;

next数组就是遇见冲突以后告诉我们要回退到哪个位置,所以称为next数组。

很多KMP算法具体实现过程中,next数组会把前缀表做一个整体-1的操作;前缀表就变成-1,0,-1,0,1,-1了;但是具体匹配过程中还会做+1操作,原理都是一样的;直接将前缀表原封不动的放入next数组中也能完成匹配。

代码实现:

有的实现是把前缀表整体右移一个位置,第一个位置变成-1;

有的实现是把前缀表整体-1;

在这里插入图片描述

文本串:aabaabaaf

模式串:aabaaf

把前缀表整体右移作为next数组:这种实现方式是直接在遇到冲突的位置也就是f所在位置进行跳转,跳转到b;

也就是在遇见冲突的时候,寻找位置的方式不一样;要么是找冲突位置的前一个位置对应的next数组值;要么就是直接找冲突位置对应的next数组值;原理都是一样的。

获得next数组分为以下几步:

  • 初始化
  • 前后缀不相同
  • 前后缀相同
  • 更新next数组
//初始化
//指针i和指针j;j是指向前缀末尾位置(最长相等前后缀的长度);i是指向后缀末尾位置
int[] next = new int[needle.length()];
int j = 0; //前缀从0开始
next[0] = j;
//求next数组:比较前缀和后缀所对应的字符是否相等,i从1开始才能和j进行比较,所以i初始化为1
for(int i = 1; i < needle.length(); i++) {
  //处理前后缀不相同的情况
  //当needle[i]不等于needle[j]的时候,就相当于在i这个位置发生了冲突,那么j就需要回退到j的前一位对应的next数组的位置;相当于在i这里发生冲突的时候,j回退到j-1对应的next[j-1]的位置,继续重新匹配前后缀
  while(s[i] != s[j]) j = next[j-1];

  //处理前后缀相同的情况
  if(s[i] == s[j]) j++;

  //更新next数组
  next[i] = j;
}

题解:


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值