字符串匹配算法之 Kmp 算法

本贴最后更新于 1944 天前,其中的信息可能已经沧海桑田

KMP 算法核心思路

kmp 算法也是一种字符串匹配算法,但是他和 BM 算法不同的是,从前往后开始匹配。共同点是都是想找出滑动最大位数的规律。

假设现在有两个字符串,一个是 abaabaabbabaaabaabbabaab,起个名叫 m,另一个 abaabbabaab 叫 p,
企业微信截图 7a2234afd6e34b9b96834a0f40ad5c44.png
两个字符串从头开始匹配的,匹配到第六个字符的时候出现不匹配的情况,这个时候把主串的第六位字符叫做坏字符,前面匹配到的叫做好前缀,这两个称呼都是针对主串的。当出现不匹配的坏字符的时候,模式串滑动多少位,能够更快的匹配完?

KMP 算法就是在视图寻找一种规律,在模式传和主串匹配的过程中,当遇到坏字符后,能否找到一种规律,将模式串一次性滑动很多位,而且匹配的字符尽可能的不再匹配。

观察主串的好前缀其实也是模式串的前缀子串,我们只要找出公共的子串,公共子串同时也是后缀子串,就可以确定滑动的位数,以上面的两个字符串为例,最长的公共子串是 ab,长度为 2,坏字符的索引是 5,将模式串滑动到公共子串的位置所需滑动的位数 5-2=3,如下图:
企业微信截图 89a59767c9d64a48a6dfbe126d2bb17f.png
这个时候需要重新,模式串中坏字符对应指针的位置,这个新位置是在好前缀公共子串的后面,也就是 公共子串最后一位字符的索引 +1.

假设坏字符对应主串的位置是 i,在模式串的位置是 j,最长公共子串,就是模式串 p[0]到 p[j]之间子串的,前缀和后缀的最长公共子串。那我们是不是可以对每个位置的模式串子串求最长公共子串,然后把它存到数组里,当主串和模式串匹配到坏字符的时候,直接从数组中取出最长公共子串,然后就可以得出滑动的位数了。

那我们来看下一下这个数组是如何构造的以及都有什么特征。

数组的值存储的是,最长可匹配前缀子串的结尾字符的下标,这里我们前缀子串和后缀子串去最长数组的下标是每个前缀子串的下标

模式字符串 a b a b a c d

模式串前缀 前缀子串结尾字符下标 最长可匹配前缀子串结尾字符下标 next 值
a 0 -1(表示不存在) next[0] = -1
a b 1 -1 next[1] = -1
a b a 2 0 next[2] = 0
a b a b 3 1 next[3] = 1
a b a b a 4 2 next[4] = 2
a b a b a c 5 -1 next[5] = -1

代码实现

public class Kmp { /** * next的数组推到过程是假设已经有部分数据计算出来,以此为基础计算后面的 * 这里说的最公共子串,指的是,模式串从开头到当前位置的子串的所有前缀子串和后缀子串的最长公共子串 * <p> * 假设模式串中0-9位对应的公共最长子串已经计算出来,此时要计算第10位字符'a'对应的最大公共子串长度 * 首先考虑加了一个字符,最大公共子串长度是否会加一,这个时候可以利用已经求出的第9位的最大公共子串, * 假设第九位最大公共子串是abaa,此时看这个前缀子串的后面一个字符是否和新字符'a'相等,如果相等,那最长公共子串长度就+1 * <p> * 如果不相等,就只能考虑最长长度不变或者减小的情况了。 * 此时要找的是,最大前缀的前缀和最大后缀加'a'字符组合的后缀的公共最长的子串了, * <p> * 最长前缀和最长后缀是一样的,那么问题就转换成,最长前缀加新字符的公共最长的子串了,就又回到了开始时的规则了 * <p> * 整体解决思路类似于一个动态规划的问题,求某个位置的所有前缀子串和所有后缀子串的公共子串长度, * 可以通过前一个位置的公最长共子串长度得出。 * * @param b * @param m * @return */ public static int[] getNexts(char[] b, int m) { int[] next = new int[m]; next[0] = -1; int j = -1; for (int i = 1; i < m; i++) { while (b[i] != b[j + 1] && j >= 0) { j = next[j]; } // 如果i位置的字符和最大前缀子串的后面一个字符相等,那么i位置的最大前缀子串长度就+1 if (b[i] == b[j + 1]) { j++; } next[i] = j; } return next; } /** * next 数组存的是当前 * 从模式串头部到当前位置的这一子串的, * 所有前缀子串和所有后缀子串匹配到最长公共子串时 * 前缀子串的最后一个字符的索引 * * @param a * @param n * @param b * @param m * @return */ public static int kmp(char[] a, int n, char[] b, int m) { int[] next = getNexts(b, m); // i 是主串指针,j是模式串指针 int j = 0; for (int i = 0; i < n; i++) { while (j > 0 && b[j] != a[i]) { j = next[j - 1] + 1; } if (a[i] == b[j]) { j++; } if (j == m) { return i - m + 1; } } return -1; } }

实力有限,如果看不懂我说的呢,就给大家推荐知乎上的一个问答,里面说的很是通俗易懂如何更好的理解和掌握 KMP 算法?前三个回答强烈建议大家细读细品。

  • 字符串
    30 引用 • 57 回帖
  • 算法
    437 引用 • 254 回帖 • 24 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3201 引用 • 8216 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    83 引用 • 37 回帖
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    66 引用 • 114 回帖 • 194 关注
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖 • 5 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 317 关注
  • 倾城之链
    23 引用 • 66 回帖 • 168 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 373 关注
  • 反馈

    Communication channel for makers and users.

    121 引用 • 907 回帖 • 273 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    693 引用 • 537 回帖 • 1 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 58 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 511 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 667 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    246 引用 • 1338 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    54 引用 • 37 回帖
  • 七牛云

    七牛云是国内领先的企业级公有云服务商,致力于打造以数据为核心的场景化 PaaS 服务。围绕富媒体场景,七牛先后推出了对象存储,融合 CDN 加速,数据通用处理,内容反垃圾服务,以及直播云服务等。

    29 引用 • 230 回帖 • 128 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 5 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    143 引用 • 442 回帖 • 1 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    170 引用 • 1529 回帖
  • 安全

    安全永远都不是一个小问题。

    203 引用 • 818 回帖 • 1 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 812 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖 • 2 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 30 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 504 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1441 引用 • 10069 回帖 • 495 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 91 关注
  • 叶归
    8 引用 • 36 回帖 • 17 关注
  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用