字符串的匹配算法_多字符串匹配

字符串的匹配算法_多字符串匹配目录需求基础知识逻辑解析源码实现需求先简单描述溪源曾经遇到的需求:需求一:项目结果文件中实验结论可能会存在未知类型、转换错误、空指针、超过索引长度等等。这里是类比需求,用日常开发中常出现的错误类型作为需求,如果要以上结论则判断这个项目检测失败;解决方案一:大家常用的方式可能是if(){continue;}esleif(){continue;}…或者switch-case等;方案二:可能会使用集合contain()方法;方案三:依次匹配字符串中字符(暴力匹配);以上两种方案都能解决;然

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

需求

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

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

解决方案一:
大家常用的方式可能是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 部分匹配值就是 0
        next[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) 满足时,部分匹配值就是+1
            if(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/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/171695.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 机器学习之Python — Sklearn库简介

    机器学习之Python — Sklearn库简介文章目录机器学习之Python—Sklearn库简介1Sklearn简介2Sklearn安装3Sklearndatasets4Sklearn通用学习模式5Sklearn数据预处理–标准化6Sklearn交叉验证7总结参考资料机器学习之Python—Sklearn库简介1Sklearn简介Scikit-learn(sklearn)是机器学习中常用的第三方模块,对常…

    2022年10月11日
    4
  • bzoj1396_bzoj3771

    bzoj1396_bzoj3771传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1396题目大意:题解:后缀自动机,只出现一次,那么就是right值为1,那么对于一段1—-L—-R来说,(L—-R)为一个最短识别子串对于(1—-L-1)则可以用R-i+1来更新,对于(L—R)则可以用R-L+1来更新,那么两个线段树来维护即可。代码:

    2022年8月12日
    6
  • centos7安装python3.7_安装python教程

    centos7安装python3.7_安装python教程文章目录前言环境&组件说明组件用途说明准备阶段安装步骤详细步骤准备安装安装Python异常处理异常信息原因分析处理方法小技巧前言工作需要,服务器不能连接外网,因此需要离线安装。推荐在线安装,参考。环境&组件说明操作系统:CentOSLinuxrelease7.4.1708(Core)操作系统安装包:CentOS-7-x86_64-Minimal-1708.isoPython版本:3.8.5pip版本:20.1.1virtualenv版本:20.4.2组件用途说

    2022年9月25日
    2
  • 在 Ubuntu 上安装Microsoft Edge[通俗易懂]

    在 Ubuntu 上安装Microsoft Edge[通俗易懂]这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好!这是你第一次使用Markdown编辑器所展示的欢迎页。如果你想学习如何使用Mar

    2022年7月21日
    31
  • spring中aop实现原理_Spring底层原理

    spring中aop实现原理_Spring底层原理写在前面:对于一个java程序员来说,相信绝大多数都有这样的面试经历,面试官问:你知道什么是aop吗?谈谈你是怎么理解aop的?等等诸如此类关于aop的问题。当然对于一些小白可能会一脸懵逼;对于一些工作一两年的,可能知道,哦!aop就是面向切面变成,打印日志啊,什么什么的,要是有点学习深度的呢可能会说aop底层实现利用了jdk动态代理,cglib啊什么的。很多时候可能面试就到此打住了,当然,然后也…

    2022年8月11日
    7
  • Nginx+PHP(laravel) 环境 499 错误码排查过程小记

    Nginx+PHP(laravel) 环境 499 错误码排查过程小记

    2022年2月17日
    46

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号