java 实现KMP算法

 更新时间:2020年12月25日 17:26:52   作者:伞菌  
这篇文章主要介绍了java 如何实现KMP算法,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下

KMP算法是一种神奇的字符串匹配算法,在对 超长字符串 进行模板匹配的时候比暴力匹配法的效率会高不少。接下来我们从思路入手理解KMP算法。

在对字符串进行匹配的时候我们最容易想到的就是一个个匹配,类似下面这种:

换成Java代码就是:

public static boolean bfSearch(String pattern,String txt){
    if (txt.length() < pattern.length())
      return false;
    for (int i = 0; i < txt.length(); i++) {
      boolean flag = false;
      for (int j = 0; j < pattern.length(); j++) {
        if (i+j>=txt.length())
          return false;
        if (txt.charAt(i+j)!=pattern.charAt(j)){
          flag = true;
        }
      }
      if (!flag){
        return true;
      }
    }
    return false;
  }

暴力匹配算法的时间复杂度为O(n*m),n为模板字符串,m为目标字符串,在处理复杂字符串时毫无疑问效率会非常低,由此诞生了KMP算法(时间复杂度为O(m+n) )。

以上面gif图中的两个字符串

​ txt = “aaacaaab”

​ pat = “aaab”

​ 为例我们来说一下KMP算法的思路。个人能力有限,您可以先行观看此视频去简单学习KMP的基本原理。

简单原理:构建状态转移数组,当遇到无法匹配的字符时根据状态转移数组进行回溯,以达到减少遍历次数的目的。

1.首先构建状态转移数组:

对匹配模式字符串进行遍历
从左向右遍历,如果 i 和 j(见源码)所指向的元素相同,则将此状态(j所指向的元素位置)进行保存
最后保存的数组是一个Int类型的状态码数组,每个元素的含义是回溯时模板字符串(pattern)的状态。

2.构建完成后通过状态转移数组和匹配模式字符串对传入的目标字符串进行匹配。过程如下:

对目标字符串进行遍历,检索相同的字符元素。
如果遇到不同的字符元素,根据 J(模板字符串的指针)所在的位置依靠状态转移数组来回溯遍历状态。并在回溯后继续进行匹配。

3.源码如下:

import java.util.Arrays;

public class KMP {
  private int[] patArray; // 状态转移数组
  private final String pattern;// 匹配模式字符串
  public static boolean bfSearch(String pattern,String txt){
    if (txt.length() < pattern.length())return false;
    for (int i = 0; i < txt.length(); i++) {
      boolean flag = false;
      for (int j = 0; j < pattern.length(); j++) {
        if (i+j>=txt.length())return false;
        if (txt.charAt(i+j)!=pattern.charAt(j)){
          flag = true;
        }
      }
      if (!flag){
        return true;
      }
    }
    return false;
  }
  KMP(String pat) { // 通过匹配模式字符串构建对象
    this.pattern = pat;
    patArray = new int[pattern.length()]; // 创建状态转移数组
    int j = 0;
    for (int i = 1; i < pattern.length(); ) {
      if (pattern.charAt(i) == pattern.charAt(j)){ 
        // 如果i和j指向的字符相同,则将此状态进行保存
        patArray[i++] = ++j; // 保存的同时移步下一位
      }else {
        // 如果 i 和 j 指向的字符不相同,则保持i不动,回溯j
        // 如果回溯后的j指向的字符与i相同,则将此时的状态进行保存
        if (j <= pattern.length() - 1 && j >0) {
          j=patArray[--j]; // 回溯j
          if (pattern.charAt(i) == pattern.charAt(j)) { 
            // 如果回溯后相同,则保存状态
            patArray[i++] = ++j;
          }
        }else if (j == 0) { 
          // 如果回溯到头,则保存为0状态
          patArray[++i] = 0;
        }
      }
    }
  }
  boolean search(String txt){
    int j = 0;
    for (int i = 0; i < txt.length(); i++) { 
      // 对输入的字符串进行模式匹配
      if (txt.charAt(i) != pattern.charAt(j)){
        // 如果匹配不成功,则根据状态数组对j进行状态更改
        if (j>0)--j; // 回溯
        j = patArray[j];
      }else {
        ++j; // 匹配成功则进入下一个状态
      }
      if (j == pattern.length()-1){ // 如果成功匹配,返回true
        return true;
      }
    }
    return false;// 匹配不成功
  }
}

后续的一些思考:

在对比较短的目标字符串而言,毫无疑问使用暴力法的效率(时间复杂度为O(M*N)会快一点,而当目标字符串的长度非常长以后,暴力匹配的效率就会大打折扣。

对于非常长的字符串来说,KMP的O(M+N)的效率相对来说就会非常高。

以上就是java 实现KMP算法的详细内容,更多关于java 实现KMP算法的资料请关注脚本之家其它相关文章!

相关文章

  • Java Servlet3.0异步处理问题

    Java Servlet3.0异步处理问题

    这篇文章主要介绍了Java中Servlet3.0异步处理的原理以及遇到的问题分析,需要的朋友参考一下。
    2017-12-12
  • 详解Java的Hibernate框架中的set映射集与SortedSet映射

    详解Java的Hibernate框架中的set映射集与SortedSet映射

    这篇文章主要介绍了详解Java的Hibernate框架中的set映射集与SortedSet映射,Hibernate是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • MyBatis如何通过拦截修改SQL

    MyBatis如何通过拦截修改SQL

    这篇文章主要介绍了MyBatis如何通过拦截修改SQL问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • mybatis spring配置SqlSessionTemplate的使用方式

    mybatis spring配置SqlSessionTemplate的使用方式

    这篇文章主要介绍了mybatis spring配置SqlSessionTemplate的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Spring JDK动态代理实现过程详解

    Spring JDK动态代理实现过程详解

    这篇文章主要介绍了Spring JDK动态代理实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 浅谈Java开发架构之领域驱动设计DDD落地

    浅谈Java开发架构之领域驱动设计DDD落地

    DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题
    2021-06-06
  • Java接口测试之日志框架Logback的具体使用

    Java接口测试之日志框架Logback的具体使用

    本文主要介绍了Java接口测试之日志框架Logback的具体使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Java中synchronized锁的深入理解

    Java中synchronized锁的深入理解

    这篇本文主要对Java中synchronized锁进行深入理解,文中通过synchronized的优化,synchronized的实现原理及synchronized的升级过程来介绍Java中synchronized锁,感兴趣的同学可以跟着小编一起来学习
    2023-05-05
  • 使用Sentinel滑动窗口实现限流和降级

    使用Sentinel滑动窗口实现限流和降级

    Sentinel 是一个开源的高可用性、高扩展性的实时流量控制框架,它可以用于保护服务稳定性,防止系统因为流量过大而崩溃,这篇文章我们所介绍的是滑动窗口,它是 Sentinel 实现限流和降级的重要组件之一,感兴趣的同学跟着小编来看看吧
    2023-09-09
  • JAVA如何转换树结构数据代码实例

    JAVA如何转换树结构数据代码实例

    这篇文章主要介绍了JAVA如何转换树结构数据代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03

最新评论