目录

  • 需求
  • 基础知识
  • 逻辑解析
  • 源码实现

需求

先简单描述溪源曾经遇到的需求:

需求一:项目结果文件中实验结论可能会存在未知类型、转换错误、空指针、超过索引长度等等。这里是类比需求,用日常开发中常出现的错误类型作为需求,如果要以上结论则判断这个项目检测失败;

解决方案一:
大家常用的方式可能是if(){continue;} esle if (){continue;} …或者switch-case等;

方案二:可能会使用集合contain()方法;

方案三:依次匹配字符串中字符(暴力匹配);

以上两种方案都能解决;然后大家需要考虑性能、维护和代码整洁性,可能居多使用方案二;

需求二:项目结论中即存在正常、成功的结论,又存在以上列举的失败字段;

例如:

//存在异常错误
String str1 = "正常范围内;转换错误";//存在异常错误
String str2 = "i=20空指针;超出索引长度;j正常";//正常值
String str3 = "i=30;j值正常";...等等

面对这种需求,大家可能会想到split()方法之后再判断是否正常等等…相信大家总是会有办法解决的。不再列举了,面对产品经理各种需求大家尽情发挥脑洞吧,那么开始进入今天的正题,溪源采用KMP字符串匹配算法解析此需求。

基础知识

根据上面介绍的需求,大家应该会对KMP算法解决的问题稍有理解。

KMP算法解决的问题:在字符串(主串)中是否能够定位出模式串(子串)。
上面提及到暴力匹配字符串,为什么不使用呢?时间复杂度O(m*n),而KMP算法时间复杂度为O(m+n)。

再介绍几个概念性的知识:

  • 前缀:除最后一位以外,第一位依次与其余字符组成的字符串集合;

  • 后缀:除第一位以外最后一位依次与其余字符组成的字符串集合;

简单举例:

字符串ABCD,其前缀:A,AB,ABC; 后缀:BCD,CD,D

  • 部分匹配值:子串的前缀和后缀共有元素的长度

简单举例:列举字符串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。

综上可以得出下面的表格:

搜索串 A B C D A B D
部分匹配值 0 0 0 0 1 2 0

逻辑解析

经历过上面的基础知识介绍后,下面开始一步步逻辑解析整个匹配过程:

  1. 字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索串(模式串,以下简称P串)"ABCDABD"的第一个字符,进行比较。
  2. 由于B与A字符不匹配,P串整体再往后移动一位与主串比较。
  3. 此时主串第二位字符B与搜索串第一位A依然不匹配,P串再继续移动…,直至主串存在与P串第一个字符匹配。
  4. 依次比较P串与主串的字符是否匹配。
  5. 匹配过程中存在与主串存在不匹配字符。
  6. 此时,大家应该是将P串再次整个后移一位,再从头逐个比较,如下图所示。虽然此种方式有效,但是效率很差,因为要把"搜索位置"移到已经比较过的位置,再次重比一遍。
  7. 从5点可以明确知道,P串中字符D与主串空格不匹配时,其实字符D之前已经匹配的六个字符是已知的。因此KMP算法思想就是利用这个已知信息,不要重复比较已经比较过的位置,而是继续将P串向后移动几位。
    重点来了,向后移动几位呢?此时便用到了上面介绍的部分匹配表

移动位数=已匹配的字符数-最后一个匹配字符对应的部分匹配值
因此,第5点之后,主串中空格与P串字符D字符不匹配时,已匹配字符为6个,最后一个以匹配字符B对应的部分匹配值为2,因此P串应该移动的位数为6-2=4。如图:

8. 空格与字符C不匹配,因此P串继续往后移。计算移动位数:已匹配的字符数为2(“AB”),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2。

9. 空格与A不匹配,继续后移一位。

10. 逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

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

源码实现

public class Kmp {/**** @param originString 源字符串* @param subString 子串* @param next 部分匹配表, 是子串对应的部分匹配表* @return 如果是-1 就是没有匹配到,否则返回第一个匹配的位置*/public static int kmpSearch(String originString, String subString, int[] next) {for (int i = 0, j = 0; i < originString.length(); i++) {while (j > 0 && originString.charAt(i) != subString.charAt(j)) {j = next[j - 1];}if (originString.charAt(i) == subString.charAt(j)) {j++;}if (j == subString.length()) {return i - j + 1;}}return -1;}/***获取到一个字符串(子串) 的部分匹配值表(前缀、后缀共同元素的长度)* @param dest 子串* @return*/public static int[] kmpNext(String dest) {//创建一个 next 数组保存部分匹配值int[] next = new int[dest.length()];//如果字符串是长度为 1 部分匹配值就是 0next[0] = 0;for (int i = 1, j = 0; i < dest.length(); i++) {while (j > 0 && dest.charAt(i) != dest.charAt(j)) {j = next[j - 1];}//当 dest.charAt(i) == dest.charAt(j) 满足时,部分匹配值就是+1if(dest.charAt(i) == dest.charAt(j)) {j++;}next[i] = j;}return next;}public static boolean matcherResult(String originString, List<String> unknownList) {boolean unknown = false;for (String unknownConclusion : unknownList) {int[] kmpNext = kmpNext(originString);int index = kmpSearch(originString, unknownConclusion, kmpNext);if (index != -1) {unknown = true;break;}}return unknown;}}

参考资料:http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/

字符串匹配算法之KMP相关推荐

  1. 字符串匹配算法(KMP)

    文章目录 1. KMP由来 2. KMP算法基本原理 3. 代码 4. Leetcode 28. 实现 strStr() 1. KMP由来 上一节说的BM算法是最高效.最常用的字符串匹配算法. 最知名 ...

  2. iptables --algo 字符串匹配算法 bm kmp

    http://blog.csdn.net/l953972252/article/details/51331001 字符串匹配一直是计算机领域热门的研究问题之一,多种算法层出不穷.字符串匹配算法有着很强 ...

  3. 字符串匹配算法:KMP算法

    KMP (Knuth–Morris–Pratt) 算法是一种改进的字符串匹配算法,它的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的. 题目:给定一个 T 字符串和一个 ...

  4. 字符串匹配算法(KMP算法JAVA版)

    目录 暴力匹配 KMP算法 暴力匹配 暴力算法就是 普通模式的匹配算法 bf算法就是将目标的字符串 的第一个字符与模式的第一个字符进行匹配,相等的话就继续比较第二个字符是否是匹配的,依次进行下去,如果 ...

  5. python匹配字符串_字符串匹配算法之Kmp算法(Python实现)

    Kmp算法的优势在于它只需要O(m)的与处理时间,而有限状态自动机最快也需要O(m * | Ʃ |).Kmp算法的主要思路跟字符串自动机很像,在预处理阶段建立一个前缀函数,然后顺序扫描文本T,即可找出 ...

  6. 《漫画算法2》源码整理-4 字符串匹配算法 RK KMP

    RK算法 public class RabinKarp {public static int rabinKarp(String str, String pattern) {//主串长度int m = ...

  7. C++实现KMP(Knuth-Morris-Pratt)字符串匹配算法

    #include"iostream"using namespace std;// 暴力匹配算法 bool string_match(char *mains,unsigned int ...

  8. diff算法阮一峰_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法

    前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...

  9. 大量的数据做字符串匹配_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法...

    前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...

最新文章

  1. 【组队学习】【29期】9. 基于transformers的自然语言处理(NLP)入门
  2. 线程池的介绍及简单实现
  3. nginx配置文件+本地测试请求转发到远程服务器+集群
  4. windows和linux的协议栈驱动
  5. Logstash 命令行参数
  6. php的用户认证(有点难度 多看几遍吧)
  7. K8S_Google工作笔记0013---通过二进制方式_部署node节点_安装docker
  8. Halcon学习路线——Blob分析(1)
  9. 老翟书摘:《丰田生产方式》
  10. MyBatis官方文档——XML配置部分
  11. opencv国内快速下载
  12. 自学C语言和C++,有什么好书推荐?
  13. iOS证书(.p12)和描述文件(.mobileprovision)申请
  14. SNAP 4. 使用snap进行地物光谱分析
  15. 产生虚假的用于欺骗的IP数据包程序实践——Teardrop
  16. (几何方面:六边形面积)编写程序,提示用户输入六边形的边长,然后输出显示它的面积。 计算六边形面积的公式是:area= s2 这里的s就是边长。下面是一个运行示例: 请输入边长:5.5
  17. 嵌入式系统中的电源管理
  18. 大年初三,字节跳动“线上免费春节档”电影再升级,13部影片登场
  19. break和continue语句、循环嵌套
  20. 2d有限元计算机仿真,超导感应电机的建模与分析

热门文章

  1. Java编程思想—第十二十三章
  2. G1垃圾收集器深度剖析
  3. flink中akka的使用 以jobClient提交任务为例子
  4. abp.net mysql_ABP .Net Core Entity Framework迁移使用MySql数据库
  5. python 字符串%和format_Python必懂知识点,格式化字符串,到底用.format还是%
  6. 收获,不止SQL优化——抓住SQL的本质--第十一章
  7. Spring Cloud 微服务实战系列-Ribbon整合RestTemplate实现负载均衡
  8. r语言集合补集_【高中数学必修1研读】之一“第一章 集合与函数概念”
  9. G1垃圾收集器之SATB
  10. [APM] 解读APM技术分类和实现方式