程序员常用十大算法之KMP算法

2023-11-17


在这里插入图片描述

本文是程序员常用十大算法的第一节,后面的算法总结都在博客里!!!

一.应用场景

 字符串匹配问题:

  1. 有一个字符串 str1= ““硅硅谷 尚硅谷你尚硅 尚硅谷你尚硅谷你尚硅你好””,和一个子串 str2=“尚硅谷你尚硅 你”
  2. 现在要判断 str1 是否含有 str2, 如果存在,就返回第一次出现的位置, 如果没有,则返回-1

二.暴力匹配算法

2.1思路分析

如果用暴力匹配的思路,并假设现在 str1 匹配到 i 位置,子串 str2 匹配到 j 位置,则有:

  1. 如果当前字符匹配成功(即 str1[i] == str2[j]),则 i++,j++,继续匹配下一个字符
  2. 如果失配(即 str1[i]! = str2[j]),令 i = i - (j - 1)j = 0。相当于每次匹配失败时,i 回溯,j 被置为 0。
  3. 用暴力方法解决的话就会有大量的回溯,每次只移动一位,若是不匹配,移动到下一位接着判断,浪费了大量 的时间。(不可行!)

2.2代码实现

package algorithm;

/**
 * @author 陌生人
 * @version V1.0
 * @Title:
 * @Package
 * @Description: (用一句话描述该文件做什么)
 * @date:
 */
public class ViolenceMatch {
   public static void main(String[] args) {
      String str1 = "硅硅谷 尚硅谷你尚硅 尚硅谷你尚硅谷你尚硅你好";
      String str2 = "尚硅谷你尚硅";
      int index = violenceMatch(str1, str2);
      System.out.println("index=" + index);
   }

   public static int violenceMatch(String s1, String s2) {
      char[] c1 = s1.toCharArray();
      char[] c2 = s2.toCharArray();
      int i = 0;
      int j = 0;
      int s1len = s1.length();
      int s2len = s2.length();
      while (i < s1len && j < s2len) {

         if (c1[i] == c2[j]) {
            i++;
            j++;
         } else {
            i = i - j + 1;
            j = 0;
         }
      }
      if (j == s2len) {
         return i - j;
      } else {
         return -1;
      }

   }
}

三.算法介绍

  1. KMP 是一个解决模式串在文本串是否出现过,如果出现过,最早出现的位置的经典算法
  2. Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP 算法”,常用于在一个文本串 S 内查找一个模式串 P 的 出现位置,这个算法由 Donald Knuth、Vaughan Pratt、James H. Morris 三人于 1977 年联合发表,故取这 3 人的 姓氏命名此算法.
  3. KMP 方法算法就利用之前判断过信息,通过一个 next 数组,保存模式串中前后最长公共子序列的长度,每次 回溯时,通过 next 数组找到,前面匹配过的位置,省去了大量的计算时间

四.KMP算法最佳应用

4.1字符串匹配问题

字符串匹配问题:

  1. 有一个字符串 str1= “BBC ABCDAB ABCDABCDABDE”,和一个子串 str2=“ABCDABD”
  2. 现在要判断 str1 是否含有 str2, 如果存在,就返回第一次出现的位置, 如果没有,则返回-1
  3. 要求:使用 KMP 算法完成判断,不能使用简单的暴力匹配算法.

4.2思路分析图解

举例来说,有一个字符串 Str1 = “BBC ABCDAB ABCDABCDABDE”,判断,里面是否包含另一个字符串 Str2 = “ABCDABD”?

  1. 首先,用 Str1 的第一个字符和 Str2 的第一个字符去比较,不符合,关键词向后移动一位## 4.3代码实现

  2. 重复第一步,还是不符合,再后移

  3. 一直重复,直到 Str1 有一个字符与 Str2 的第一个字符符合为止
    4.

  4. 接着比较字符串和搜索词的下一个字符,还是符合。
    在这里插入图片描述

  5. 遇到 Str1 有一个字符与 Str2 对应的字符不符合。
    在这里插入图片描述

  6. 这时候,想到的是继续遍历 Str1 的下一个字符,重复第 1 步。(其实是很不明智的,因为此时 BCD 已经比较过了, 没有必要再做重复的工作,一个基本事实是,当空格与 D 不匹配时,你其实知道前面六个字符是”ABCDAB”。 KMP 算法的想法是,设法利用这个已知信息,不要把”搜索位置”移回已经比较过的位置,继续把它向后移,这 样就提高了效率。)

在这里插入图片描述

  1. 怎么做到把刚刚重复的步骤省略掉?可以对 Str2 计算出一张《部分匹配表》,

在这里插入图片描述

  1. 已知空格与 D 不匹配时,前面六个字符”ABCDAB”是匹配的。查表可知,最后一个匹配字符 B 对应的”部分 匹配值”为 2,因此按照下面的公式算出向后移动的位数:
    移动位数 = 已匹配的字符数 - 对应的部分匹配值
    因为 6 - 2 等于 4,所以将搜索词向后移动 4 位。
  2. 因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为 2(”AB”),对应的”部分匹配值” 为 0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移 2 位。
    在这里插入图片描述

10.因为空格与 A 不匹配,继续后移一位。
在这里插入图片描述

  1. 逐位比较,直到发现 C 与 D 不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动 4 位。
    在这里插入图片描述

  2. 逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数=7-0,再将搜索词向后移动7位,这里就不再重复了。
    在这里插入图片描述

  3. 介绍《部分匹配表》怎么产生的先介绍前缀,后缀是什么
    在这里插入图片描述

“部分匹配值”就是”前缀”和”后缀”的最长的共有元素的长度。
以”ABCDABD”为例,
”A”的前缀和后缀都为空集,共有元素的长度为0;
”AB”的前缀为[A],后缀为[B],共有元素的长度为0;
”ABC”的前缀为[A,AB],后缀为[BC,C],共有元素的长度0;
”ABCD”的前缀为[A,AB,ABC],后缀为[BCD,CD,D],共有元素的长度为0;
”ABCDA”的前缀为[A,AB,ABC,ABCD],后缀为[BCDA,CDA,DA,A],共有元素为”A”,长度为1;
”ABCDAB”的前缀为[A,AB,ABC,ABCD,ABCDA],后缀为[BCDAB,CDAB,DAB,AB,B],共有元素为”AB”,长度为2;
”ABCDABD”的前缀为[A,AB,ABC,ABCD,ABCDA,ABCDAB],后缀为[BCDABD,CDABD,DABD,ABD,BD,D],共有元素的长度为0。

  1. ”部分匹配”的实质是,有时候,字符串头部和尾部会有重复。
    比如,”ABCDAB”之中有两个”AB”,那么它的”部分匹配值”就是2(”AB”的长度)。搜索词移动的时候,第一个”AB”向后移动4位(字符串长度-部分匹配值),就可以来到第二个”AB”的位置。
    在这里插入图片描述

代码实现

package algorithm;

import java.util.Arrays;

/**
 * @author 陌生人
 * @version V1.0
 * @Title:
 * @Package
 * @Description: (用一句话描述该文件做什么)
 * @date:
 */
public class KMPAlgorithm {
   public static void main(String[] args) {
      String str1 = "BBCABCDABABCDABCDABDE";
      String str2 = "ABCDABD";
//Stringstr2="BBC";
      int[] next = kmpNext("ABCDABD");//[0,1,2,0]
      System.out.println("next=" + Arrays.toString(next));
      int index = kmpSearch(str1, str2, next);
      System.out.println("index=" + index);//15了

   }

   public static int kmpSearch(String str1, String str2, int[] next) {
      //遍历
      for (int i = 0, j = 0; i < str1.length(); i++) {
         //需要处理
         // str1.charAt(i)!=str2.charAt(j),去调整j的大小
         // KMP算法核心点,可以验证...
         while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
            j = next[j - 1];
         }

         if (str1.charAt(i) == str2.charAt(j)) {
            j++;
         }
         if (j == str2.length()) {//找到了//j=3i
            return i - j + 1;
         }
      }
      return -1;
   }

   //获取到一个字符串(子串)的部分匹配值表
   public static int[] kmpNext(String dest) {
      //创建一个next数组保存部分匹配值
      int[] next = new int[dest.length()];
      next[0] = 0;
      //如果字符串是长度为1部分匹配值就是0
      for (int i = 1, j = 0; i < dest.length(); i++) {
         //当dest.charAt(i)!=dest.charAt(j),我们需要从next[j-1]获取新的j
         // 直到我们发现有dest.charAt(i)==dest.charAt(j)成立才退出
         // 这时kmp算法的核心点
         while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
            j = next[j - 1];
         }

         //当dest.charAt(i)==dest.charAt(j)满足时,部分匹配值就是+1
         if (dest.charAt(i) == dest.charAt(j)) {
            j++;
         }
         next[i] = j;
      }
      return next;
   }
}

长路漫漫,JAVA为伴!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

程序员常用十大算法之KMP算法 的相关文章

  • 算法题-简单系列-07-判断一个链表是否为回文结构

    文章目录 1 题目 1 1 使用list集合判断 1 题目 给定一个链表 请判断该链表是否为回文结构 回文是指该字符串正序逆序完全一致 1 1 使用list集合判断 因为需要判断是否为回文结构 所以要比较头尾的数据 而链表无法随机查询数据
  • 算法题-简单系列-01-链表反转

    文章目录 1 题目 1 1 使用栈解决 1 2 反转链表 1 题目 给定一个单链表的头结点pHead 该头节点是有值的 比如在下图 它的val是1 长度为n 反转该链表后 返回新链表的表头 如当输入链表 1 2 3 时 经反转后 原链表变为
  • 算法题-简单系列-06-删除有序链表中重复的元素

    文章目录 1 题目 1 1 循环遍历 1 题目 1 1 循环遍历 既然连续相同的元素只留下一个 我们留下哪一个最好呢 当然是遇到的第一个元素了 因为第一个元素直接就与前面的链表节点连接好了 前面就不用管了 只需要跳过后面重复的元素 连接第一
  • Radix Tree用法

    目录 一 radix tree定义 二 radix tree操作 参考资料 一 radix tree定义 对于长整型数据的映射 如何解决Hash冲突和Hash表大小的设计是一个很头疼的问题 radix树就是针对这种稀疏的长整型数据查找 能快
  • 数组实现循环队列(增设队列大小size)

    目录 一 前言 1 如何实现循环 2 如何判断队列为空 3 如何判断队列为满 二 循环队列的结构定义 三 循环队列的创建及其初始化 四 入队 五 出队 六 取队头元素 七 取队尾元素 八 循环队列判空 九 循环队列判满 十 循环队列销毁 一
  • c 关于数组几个查序程序

    1 查询某元素是否在数组中 int main void char i 10 2 1 7 2 10 5 2 0 1 4 10 10 1 3 1 0 8 char z 10 1 2 3 4 1 4 6 8 0 9 int zz 0 标志位 0
  • 【数据结构】单链表的定义和操作

    目录 1 单链表的定义 2 单链表的创建和初始化 3 单链表的插入节点操作 4 单链表的删除节点操作 5 单链表的查找节点操作 6 单链表的更新节点操作 7 完整代码 嗨 我是 Filotimo 很高兴与大家相识 希望我的博客能对你有所帮助
  • 每日一练2023.12.17——大笨钟的心情【PTA】

    题目链接 L1 077 大笨钟的心情 题目要求 有网友问 未来还会有更多大笨钟题吗 笨钟回复说 看心情 本题就请你替大笨钟写一个程序 根据心情自动输出回答 输入格式 输入在一行中给出 24 个 0 100 区间内的整数 依次代表大笨钟在一天
  • List去重-使用distinctByKey方法根据对象的属性进行去重

    description 使用distinctByKey方法根据对象的属性进行去重 author zs date 2023 12 18 14 29 param keyExtractor return java util function Pr
  • leetcode 560. 和为 K 的子数组(优质解法)

    代码 class Solution public int subarraySum int nums int k int length nums length key 表示前缀和 value 表示个数 HashMap
  • 回溯算法第零篇【递归、搜索与回溯】【回溯算法入门必看】

    本篇文章的目的 1 给小伙伴们对回溯算法中的名词进行解释 2 消除递归的恐惧 回溯是递归的一个分支 给小伙伴们一个建议 整篇文章都要看完 一字不漏 全是干货 注意 分析回溯的思想之前 我们得知道一个关系 递归包含搜索 搜索包含回溯 所以我们
  • 代码随想录算法训练营第42天| ● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集

    416 分割等和子集 已解答 中等 相关标签 相关企业 给你一个 只包含正整数 的 非空 数组 nums 请你判断是否可以将这个数组分割成两个子集 使得两个子集的元素和相等 示例 1 输入 nums 1 5 11 5 输出 true 解释
  • 回溯算法第零篇【递归、搜索与回溯】【回溯算法入门必看】

    本篇文章的目的 1 给小伙伴们对回溯算法中的名词进行解释 2 消除递归的恐惧 回溯是递归的一个分支 给小伙伴们一个建议 整篇文章都要看完 一字不漏 全是干货 注意 分析回溯的思想之前 我们得知道一个关系 递归包含搜索 搜索包含回溯 所以我们
  • LeetCode21. Merge Two Sorted Lists

    文章目录 一 题目 二 题解 一 题目 You are given the heads of two sorted linked lists list1 and list2 Merge the two lists into one sort
  • LeetCode 1901. 寻找峰值 II

    一 题目 1 题目描述 一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子 上 下 左 右 的元素 给你一个 从 0 开始编号 的 m x n 矩阵 mat 其中任意两个相邻格子的值都 不相同 找出 任意一个 峰值 mat i j
  • LeetCode 2397. 被列覆盖的最多行数,状态压缩优化回溯法

    一 题目 1 题目描述 给你一个下标从 0 开始 大小为 m x n 的二进制矩阵 matrix 另给你一个整数 numSelect 表示你必须从 matrix 中选择的 不同 列的数量 如果一行中所有的 1 都被你选中的列所覆盖 则认为这
  • 搜索二叉树(BSTree)

    一 搜索二叉树的概念 二叉搜索树又称为做二叉排序树 二叉查找树 其要么是一棵空树 要么是一个满足以下性质的二叉树 若它的左子树不空 则左子树上所有结点的关键字均小于根结点关键字 若它的右子树不空 则右子树上所有结点的关键字均大于根结点关键字
  • 链表的中间节点

    链表的中间节点 力扣 LeetCode 官网 全球极客挚爱的技术成长平台 备战技术面试 力扣提供海量技术面试资源 帮助你高效提升编程技能 轻松拿下世界 IT 名企 Dream Offer https leetcode cn problems
  • 顺序表和链表基础

    定义动态的顺序表 typedef int SLDataType typedef struct Seqlist SLDataType array size t size size t capacity Seqlist 在顺序表中插入数据 bo
  • 「优选算法刷题」:移动零

    嗨 这个假期罗根开始接触了算法 在为今年的蓝桥杯做准备 所以 开个新专栏 记录记录自己做算法题时的心得 一 题目 给定一个数组 nums 编写一个函数将所有 0 移动到数组的末尾 同时保持非零元素的相对顺序 请注意 必须在不复制数组的情况下

随机推荐

  • SQL Server(四) - 插入、更新和删除数据

    1 主要内容 通过SSMS 插入 更新和删除表数据 通过INSERT语句向表中插入数据 通过UPDATE语句更新表内数据 通过DELETE语句删除表内数据 使用INSERT UPDATE和DELETE语句的几个技巧2 使用INSERT语句插
  • Java lang3的 StringUtils.isNumeric(str)不能识别负数和小数

    Java lang3的 StringUtils isNumeric str 不能识别负数和小数 StringUtils isNumeric null false StringUtils isNumeric false StringUtils
  • 带你入门使用SSM+Thymeleaf先感受下基本运行和什么是SpringMVC吧

    通俗点讲SSM框架指的是Spring Boot Spring MVC Mybatis Thymeleaf是一个页面模板 这篇文章旨在 教会创建一个SSM项目 和配合Thymeleaf进行项目开发 此文章图多 所以长度很长 但干货满满 如果想
  • 前置路由守卫

    路由守卫 他是一个方法 拦截路由 路由守卫写在路由暴露之前 在这个方法中会传递一个回调函数 回调函数中传递3个参数 router beforeEach gt 也叫做前置路由守卫 里面有3个参数 to 到哪里去 from 从哪里来 next
  • JAVA基础之增删改查

    开发工具 eclipse 数据库 Oracle 项目分析 数据库的搭建 项目结构 一 主界面 采用无刷新的方法 该方法需要用到jQuery插件 界面展示 分页 删除 模糊查询 教员 班级 爱好 代码展示
  • 递推典型算法:猴子爬山,跳台阶,爬楼梯(牛客网)、魔法深渊(快手)----Python、Java

    递推算法的基本思想是把一个复杂的 庞大的计算过程转化为简单过程的多次重复 其首要问题是得到相邻的数据项之间的关系 即递推关系 以猴子爬山为例 1 问题的提出 一个顽猴在一座有30级太假的小山上爬山活跃 猴子上一步可跳1级或者3级 试求上山的
  • win10黑屏假死怎么解决

    现在很多用户都是使用的win10电脑 但是电脑使用久了之后出现一些问题时在所难免的 例如最近就有网友反映说自己的win10纯净版电脑出现了开机后黑屏假死的情况 该如何处理呢 赶紧看看吧 解决教程如下 1 win10系统开机的时候一直按F8直
  • 【Python】python使用docxtpl生成word模板

    python使用docxtpl生成word模板 python docxtpl包简单使用和实战 Python处理word docx文件 最近需要处理一些爬虫得到的数据来进行一些自动化报告的操作 因为需要生成的是word的报告 所以估选用doc
  • Mastering_Rust(译):内存,生命周期和借用(完+1)

    Rust让你 开发人员自己处理内存 但是 它可以帮助您完成内存分配的抽象和语言支持 它的生命周期 所有权和借用系统可能是您熟悉的C 世界的概念 Rust拥有所有这些 不仅仅是概念 而是语言以及编译时检查 使这一类最困难的运行时问题变得更容易
  • 神经网络调优 --- batch_size

    batchsize和收敛速度 性能的关系 一般来说 在合理的范围之内 越大的 batch size 使下降方向越准确 震荡越小 小的 bath size 引入的随机性更大 单次epoch耗时更久 且震荡大难以收敛 但大的batchsize容
  • SQL注入漏洞简介、原理及防护

    目录 1 SQL注入漏洞简介 2 SQL注入漏洞原理 3 SQL注入的分类 4 注入方法 5 SQL注入危害 6 SQL注入防护措施 1 SQL注入漏洞简介 SQL注入漏洞是Web层面最高位的漏洞之一 所谓SQL注入 就是通过把SQL命令插
  • Ubunto18.04安装完成后没有gcc、make、g++、无法访问域名等解决方法

    Ubunto18 04安装完成后没有gcc make g 无法访问域名等解决方法 今天新安装完虚拟机后又安装了Ubunto 然后就碰到了个恶心的事情 就是这是个纯净版 里面好多东西都没有 没有gcc make等等 按照其他博主的方法都不好使
  • 本地电脑与阿里云服务器之间实现文件传输

    文章目录 1 文件传输 1 1 服务器使用本地文件 1 2 使用FileZilla软件 1 2 1 服务端配置 1 2 2 客户端配置 1 2 3 问题及解决 1 2 3 1 bug 1 2 3 2 原因 1 2 3 3 解决 1 2 3
  • 解决“ warning C276: constant in condition expression“和“warning C294: unreachable code“两个告警提示

    好久不见 最近因为一些项目比较忙 都没怎么发文章了 今天有空就来分享一下自己之前遇到的两个告警 告警信息 首先 我们先来看一下这两个告警 第一个告警为 warning C276 constant in condition expressio
  • Go Web编程实战(3)----数据类型

    目录 前言 布尔型 数字类型 字符串类型 使用 byte修改 使用 rune修改 指针类型 指针的简单用法 修改指针值 复合类型 数组类型 结构体介绍 切片类型 从指定范围生成切片 重置切片 直接声明切片 Map 前言 Go语言数据类型包括
  • 设置idea控制台打印的日志输出到本地文件

    在idea的控制台很难看到日志 很快速的就刷过去了 而且日志多的话 很多就看不到了 所以我设置了一下idea把日志输出到文件 方便查看 1 在idea的菜单栏 找到这个向下的三角 点击 选择Edit Configurations 2 在打开
  • 官宣!玖章算术获评“浙江省创新型中小企业”

    近日 浙江省工业和信息化厅开展了 2023 第二季度创新型中小企业评价工作 经企业自评 地市审核 抽查 市经信局审核评价等程序 玖章算术以优秀的自主创新能力通过认定 成为浙江省 2023 年度创新型中小企业 创新型中小企业 是指具有较健全的
  • FISCO BCOS(五)———部署安装jdk1.8

    1 将下载的jdk1 8 0 162 linux x64 tar gz通过远程连接工具 放入 usr local 目录 然后解压 2 解压 tar zxvf jdk1 8 0 162 linux x64 tar gz 3 切换到jdk1 8
  • 闲聊——集成学习理论(Adaboost,随机森林理论与个人实战中的体会)

    本文通过简单的例子来引出算法本质 同时附上证明过程 目的是让感觉直接看证明推导很难的小伙伴们也能理解集成算法是怎样实现的 集成学习通过构建并结合多个学习器来完成学习任务 可获得比单一学习器更好的泛化性能 目前的集成学习方法大致可分为两类 第
  • 程序员常用十大算法之KMP算法

    程序员常用十大算法之KMP算法 一 应用场景 二 暴力匹配算法 2 1思路分析 2 2代码实现 三 算法介绍 四 KMP算法最佳应用 4 1字符串匹配问题 4 2思路分析图解 代码实现 本文是程序员常用十大算法的第一节 后面的算法总结都在博