当前位置: 首页 > wzjs >正文

做网站建设哪家公司好郑州做网站推广资讯

做网站建设哪家公司好,郑州做网站推广资讯,注册一个公司,discuz!网站模板🎯 本文详细解析了“查找字符串首个匹配项下标”问题的多种解法,包括调用API、暴力求解和KMP算法。调用API如indexOf方法可一行代码实现,简单高效但可能缺乏面试深度。暴力求解通过逐字符对比完成匹配,时间复杂度为O(m*n)&#xf…

🎯 本文详细解析了“查找字符串首个匹配项下标”问题的多种解法,包括调用API、暴力求解和KMP算法。调用API如indexOf方法可一行代码实现,简单高效但可能缺乏面试深度。暴力求解通过逐字符对比完成匹配,时间复杂度为O(m*n),适合理解基础逻辑。KMP算法利用最长相同前后缀信息优化匹配过程,显著提高效率,适用于大规模字符串处理场景,时间复杂度为O(m+n)。文章结合案例深入讲解了KMP算法中前缀表的构建与应用,帮助读者全面掌握字符串匹配的核心技巧。

找出字符串中第一个匹配项的下标

题目

题目链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/

在这里插入图片描述

调用API

这道题调用 API ,一行代码就可以搞定,效率还贼高。只是这样写,可能会被面试官屌一顿

public int strStr(String haystack, String needle) {return haystack.indexOf(needle);
}

在这里插入图片描述

暴力求解

为了方便理解,haystack在本文中使用text表示,needle在本文中用pattern表示。

【思路】

  1. 遍历文本串:从文本串的第一个字符开始,逐个字符作为匹配的起点。
  2. 尝试匹配模式串
    1. 对于文本串的每一个起点,依次比较文本串和模式串的每一个字符。
    2. 如果所有字符都匹配,说明找到了模式串,返回匹配的起始位置。
    3. 如果某个字符不匹配,则停止当前起点的匹配,移动到文本串的下一个起点,重新开始匹配。
  3. 结束条件
    1. 如果遍历完文本串的所有可能的起点,仍未找到匹配的子串,则返回 -1 。

假设text=“abcabeabcabfy”,pattern=“abcabf”,使用暴力方法求解过程如下:

在这里插入图片描述

/*** 暴力破解** @param text* @param pattern* @return*/
public int strStr(String text, String pattern) {for (int i = 0; i < text.length(); i++) {boolean found = true;for (int j = 0; j < pattern.length(); j++) {if (i + j > text.length() - 1 || text.charAt(i + j) != pattern.charAt(j)) {found = false;break;}}if (found) {return i;}}return -1;
}

时间复杂度是O(m*n),m 是 text 的长度,n 是 pattern 的长度。虽然复杂度高,但是你别说,暴力求解还真可以 AC 。

在这里插入图片描述

KMP 算法

KMP 算法原理

KMP 算法的思想是:当出现字符不匹配时利用部分已经匹配过的内容信息来避免pattern从头开始匹配。

还是用上面的数据:text=“abcabeabcabfy”,pattern=“abcabf”,KMP算法计算过程如下:

在这里插入图片描述

KMP 和暴力破解的区别是:

  • 暴力求解:pattern的指针直接回到 0 ,text的指针直接指向下一个起点
  • KMP:指针直接跳过pattern已经匹配过的部分,从下一个索引开始匹配

在这里插入图片描述

KMP为什么可以这样做呢?

如下图所示,两个指针分别指向 e 和 f 时,两者不匹配了,但是abcabeabcabf的前面部分是匹配的,即abcab。通过找abcab的相同前后缀,可以发现text1text2相同。又因为在前面的匹配中,已经知道text3text2肯定是匹配的了,因此text3text1也一定匹配。

在这里插入图片描述

为了提高算法效率,就可以省略text3text1的匹配计算了,直接从下一个字符开始匹配,即 c 和 e 。

在这里插入图片描述

好,到这里,我们就已经知道 KMP 效率高的原因了,就是利用好相同前后缀的信息来跳过不必要的遍历。那我们如何记录相同前后缀信息呢,总不能每次遇到不匹配的字符,就去计算相同前后缀是吧,那这样效率就不高了。

记录相同前后缀信息

首先定义一下前缀和后缀

  • 前缀:包括pattern的第一个字母,不包括pattern的最后一个字母的所有子串,如aabaaf有如下前缀
    • aaaaabaabaaabaa
  • 后缀:包括pattern的最后一个字母,不包括pattern的第一个字母的所有子串,如aabaaf有如下后缀
    • fafaafbaafabaaf

为了在计算的时候,能快速知道相同前后缀的长度是多少,我们**使用****前缀表**来记录该最长相同前后缀信息。假设pattern=aabaaf,前缀表如下:

pattern截取范围子串(pattern[0,i]最长相同前后缀原因
[0,1)、[0,0]a0只有一个字符,没有前缀,也没有后缀,所以长度是0
[0,2)、[0,1]aa1a 与 a,为什么是1,不是2 ?由上面的定义可知:aa的前缀只有 a
[0,3)、[0,2]aab0
[0,4)、[0,3]aaba1a 与 a
[0,5)、[0,4]aabaa2aa 与 aa
[0,6)、[0,5]aabaaf0

后面匹配到不同的字符时,就可以凭借前缀表快速找到已匹配部分的最长相同前后缀长度是什么。如下图所示,当匹配到 e ≠ f 时,此时pattern的指针指向索引 5 ,那么根据[0,5),可以在前缀表立刻找到aabaa的最长相同前后缀长度为 2 。

在这里插入图片描述

则下一步,直接将pattern的指针指向最长相同前后缀长度 2 ,即指向 b

在这里插入图片描述

OK,讲了这么多,也知道 前缀表 的作用是什么了,那么如何记录和填充前缀表呢?

前缀表存储

关于记录前缀表,直接使用一个 int 数组即可,将这个数组命名为 next ,如下图所示:

在这里插入图片描述

前缀表填充

前缀表填充逻辑如下:

  1. 初始化next[0] = 0,此时子串只有一个字符,不存在前缀和后缀,因此最长相同前后缀长度为 0 。再定义两个指针,i表示当前字符的位置,j表示尝试前缀的末尾位置。
    1. i初始化为 1 ,因为next[0] = 0,直接从 1 开始填充即可。
    2. j初始化为 0 ,因为i = 1时,子串的前缀只有第 0 个字符,只能尝试将 0 个字符作为前缀。
  2. 遍历模式串:从i = 1开始,逐个字符计算next[i]
  3. 匹配
    1. 如果pattern[i] == pattern[j],说明当前字符与尝试前缀字符匹配。
      • next[i] = j + 1(为啥是 j+1,例如aa,此时j=0,但是共同前后缀长度是 1 )。
      • 然后ij都向后移动,即i++j++
    2. 如果pattern[i] != pattern[j],说明当前字符与前缀字符不匹配
      • 回退jnext[j-1],再重新比较pattern[i]pattern[j]。直到j回退到0或找到匹配的前缀。
      • 为什么回退到next[j-1]?因为next[j-1]表示的是已匹配部分的最长相同前后缀长度,回退就是想看看将前缀缩短为上一次匹配成功的共同前后缀,因为有可能共同前后缀还是有效的,如果不理解,看后面的演示案例。
  4. 不匹配情况:如果j回退到0,说明没有匹配的前后缀,next[i] = 0,然后i向后移动。
  5. 重复步骤3和4,直到遍历完整个模式串。

【案例】

初始化 next[0] = 0

在这里插入图片描述

① 初始化 i=1j=0

  • pattern[i]=pattern[j]=a,即前缀a等于后缀anext[i]=++j=1

在这里插入图片描述

  • 因为pattern[i]=pattern[j],i++, j++

i=2j=1

  • pattern[i]!=pattern[j],即前缀aa不等于后缀ab,把前缀缩短,j=next[j-1]=0

在这里插入图片描述

  • pattern[i]!=pattern[j],即前缀a不等于后缀b,因为 j 已经走到 0 ,next[i]=0

在这里插入图片描述

  • 因为pattern[i]!=pattern[j],只让 i++

i=3j=0

  • pattern[i]=pattern[j]=‘a’,即前缀a等于后缀anext[i]=++j=1

在这里插入图片描述

  • 因为pattern[i]=pattern[j],i++, j++

i=4j=1

  • pattern[i]=pattern[j]=‘a’,即前缀aa等于后缀aanext[i]=++j=2

在这里插入图片描述

  • 因为pattern[i]=pattern[j],i++, j++

i=5j=2

  • pattern[i]!=pattern[j],即前缀aab不等于后缀aaf,把尝试前缀缩短,j=next[j-1]=1

在这里插入图片描述

  • pattern[i]!=pattern[j],即前缀aa不等于后缀af,把前后缀缩短,j=next[j-1]=0

在这里插入图片描述

  • pattern[i]!=pattern[j],即前缀a不等于后缀f,把前后缀缩短,j 已经走到 0 ,next[i]=0

在这里插入图片描述

【案例】

再举一个例子,看看为什么要将j回退到next[j-1]

next[j-1]表示的是已匹配部分的最长相同前后缀长度,回退就是想看看将前缀缩短为上一次匹配成功的共同前后缀,现在看看还生不生效,其实也是利用前面的信息进行贪心的做法。

如下图所示:

  • b!=a,将j回退为next[j-1]=1,这样很快就可以得到最长相同前后缀是aa,而不是直接将j回退到 0 重新开始寻找最长相同前后缀。

在这里插入图片描述

next 数组填充代码实现

知道上面的逻辑之后,代码就很容易实现了

char[] patternCharArray = pattern.toCharArray();
int patternLen = pattern.length(); 计算 next 数组
int[] next = new int[patternLen];
int j = 0;
for (int i = 1; i < patternLen; i++) {while (true) {if (patternCharArray[i] == patternCharArray[j]) {// --if-- 如果pattern[i] == pattern[j],说明当前字符与前缀字符匹配next[i] = j + 1;// i++ 已经在 for 循环中实现了j++;break;} else {// --if-- 如果pattern[i] != pattern[j],说明当前字符与前缀字符不匹配if (j == 0) {// 本来就是0,不用设置
//                        next[i] = 0;break;}// 回退j到next[j-1]j = next[j - 1];}}
}

KMP搜索

得到了 next 数组之后,剩下的代码就更好写了,逻辑如下:

双指针遍历

  • index1 遍历主字符串,index2 遍历模式字符串。

尝试匹配 text[index1] pattern[index2]

  1. 匹配成功

    1. 字符匹配时,双指针同时前进。
    2. index2 走完模式串,返回匹配的起始位置 index1 - patternLen + 1
      在这里插入图片描述
  2. 匹配失败

    1. index2 为 0,主指针 index1 前进。
    2. 否则,index2 回退到 next[index2 - 1] 继续匹配。
      在这里插入图片描述

最终代码实现

public static int strStr(String text, String pattern) {char[] textCharArray = text.toCharArray();char[] patternCharArray = pattern.toCharArray();int patternLen = pattern.length();if (patternLen > textCharArray.length) {// pattern 比 text 还长,直接返回-1return -1;} 计算 next 数组int[] next = new int[patternLen];int j = 0;for (int i = 1; i < patternLen; i++) {while (true) {if (patternCharArray[i] == patternCharArray[j]) {// --if-- 如果pattern[i] == pattern[j],说明当前字符与前缀字符匹配next[i] = j + 1;// i++ 已经在 for 循环中实现了j++;break;} else {// --if-- 如果pattern[i] != pattern[j],说明当前字符与前缀字符不匹配if (j == 0) {// 本来就是0,不用设置
//                        next[i] = 0;break;}// 回退j到next[j-1]j = next[j - 1];}}} 查找 pattern 位置int index1 = 0, index2 = 0;while (index1 < textCharArray.length) {if (textCharArray[index1] == patternCharArray[index2]) {// --if-- 如果当前字符匹配,两个指针都前进index1++;index2++;if (index2 == patternLen) {// --if-- pattern的字符已经匹配完,返回索引(因为这里 index1++ 才-patternLen ,所以不用+1)return index1 - patternLen;}} else {if (index2 == 0) {// --if-- pattern 指针已经归零,无法再回退了index1++;continue;}// 回退 pattern 指针index2 = next[index2 - 1];}}return -1;
}

在这里插入图片描述

时间复杂度O(n+m)

假设n为text长度,m为pattern长度,在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n)。除此之外,还生成next数组,时间复杂度是O(m)

http://www.dtcms.com/wzjs/32637.html

相关文章:

  • 淘宝网站制作公司seo快速排名工具
  • 网上做网站的靠谱吗短视频赚钱app软件
  • 公司排名100强网站seo谷歌
  • 做企业网站 长春品牌网络seo方案外包
  • 美女做暖暖的视频网站破解版百度网盟
  • 房屋模拟装修软件网站优化网络推广seo
  • 网页设计实验报告结果短视频seo
  • 做恶搞网站软件如何制作网页
  • 桓台网站建设产品推广策划方案
  • 怎样做风水网站百度一下百度主页官网
  • 乌鲁木齐新疆网站建设公司微信推广平台
  • 佛山微信网站建设多少钱网络推广公司哪里好
  • 网站建设语上海优化公司
  • 昆明云南微网站建设超级优化大师
  • qwins是哪个网站做的seo谷歌
  • 建设电子商务网站论文东莞seo外包公司哪家好
  • 教育网站如何做seo宁波seo优化定制
  • 动态ip建网站影视后期哪个培训靠谱
  • 网站推广怎么做与发布网站搜索引擎优化的方法
  • 室内设计者联盟网站徐州百度推广总代理
  • 网站建设公司怎么找业务品牌推广策划方案案例
  • 网站建立的研究方案公司网站域名怎么注册
  • 织梦修改网站源代码站内营销推广方式
  • 友情链接做自己的网站在线代理浏览网站免费
  • 楼书设计素材网站企业网站搜索引擎推广方法
  • 女生做网站编辑好还是搜索引擎排名优化包括哪些方面
  • 网站建设与维护 国赛广告网站留电话不用验证码
  • 哈尔滨建设局网站seo全网营销
  • 在线视频网站a做免费下载seo常规优化
  • 北京网站系统开发市场调研方法有哪些