java 实现KMP算法

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算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • java 中模式匹配算法-KMP算法实例详解

    java 中模式匹配算法-KMP算法实例详解 朴素模式匹配算法的最大问题就是太低效了.于是三位前辈发表了一种KMP算法,其中三个字母分别是这三个人名的首字母大写. 简单的说,KMP算法的对于主串的当前位置不回溯.也就是说,如果主串某次比较时,当前下标为i,i之前的字符和子串对应的字符匹配,那么不要再像朴素算法那样将主串的下标回溯,比如主串为"abcababcabcabcabcabc",子串为"abcabx".第一次匹配的时候,主串1,2,3,4,5字符都和子串相应的

  • Java sm3加密算法的实现

    1.准备工作 所需jar包: bcprov-jdk15on-1.59.jar commons-lang3-3.1.jar 对应的maven依赖 <!--sm3,sm4加密算法--> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.66</version> <

  • 如何通过Java代码实现KMP算法

    这篇文章主要介绍了如何通过Java代码实现KMP算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特--莫里斯--普拉特操作(简称KMP算法).KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.具体实现就是实现一个next()函数, 函数本身包含了模式串的局部匹配信息.时间复

  • java实现微信抢红包算法

    简介 网上说的有两种比较公平的算法,一种是二倍均值法,一种是线段切割法.下面我们介绍下两种算法的实现: 二倍均值法 原理 剩余红包金额M,剩余人数N,那么:每次抢到金额=随机(0,M/N*2) 保证了每次随机金额的平均值是公平的 假设10人,红包金额100元 第一人:100/10*2=20,随机范围(0,20),平均可以抢到10元 第二人:90/9*2=20,随机范围(0,20),平均可以抢到10元 第三人:80/8*2=20,随机范围(0,20),平均可以抢到10元 以此类推,每次随机范围的均

  • Java MD5消息摘要算法原理及实现代码

    md5 属于hash算法一类,是不可逆的消息摘要算法.与对称加密和非对称加密算法不一样,不需要加密密钥. 注意: md5不是加密算法,只是将数据进行散列计算后生成一个唯一值的算法,没有加密密钥也没有解密密钥. 下面说的md5加密是指对密码加密成32位长度字符串的过程 md5可以用于密码的加密,如123456,加密后的字符串,在很大条件下不能被电脑强行破解出来,只能通过字典匹配的方式同样用md5加密后的字符串进行比较破解. MessageDigest消息摘要是安全的单向散列函数,它将任意大小的字符

  • Java sha1散列算法原理及代码实例

    直接调用HashKit.sha1(String str)方法就可以了,,返回的是16进制的字符串长度是40, 也就是用md.digest()方法解析出来的字节数是160字节长度. 而MD5散列算法生成的字节数是128字节长度,返回的16进制的字符长度是32位 代码如下 public class HashKit { private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); public stati

  • java中利用栈实现字符串回文算法

    问题 给定一个由多个a和b组成的字符串数组,字符串中有一个特殊的字符X,位于字符串的正中间,例如(aaaabbbbXabaabbbb),如何判定该字符串是否回文 简单算法 定义两个下标分别指向字符串的头和尾,每次比较两个下标位置的值是否相等,如果不相等,那么输入的 字符串不是回文,如果相等,左边的下表加1,右边的下表减1,重复上述步骤直至两个下标都指向字符串的正中间或者确定字符串不是回文 /** * 判断字符串是否是回文 */ public int isPalindrome(String inp

  • JAVA实现KMP算法理论和示例代码

    一.理论准备KMP算法为什么比传统的字符串匹配算法快?KMP算法是通过分析模式串,预先计算每个位置发生不匹配的时候,可以省去重新匹配的的字符个数.整理出来发到一个next数组, 然后进行比较,这样可以避免字串的回溯,模式串中部分结果还可以复用,减少了循环次数,提高匹配效率.通俗的说就是KMP算法主要利用模式串某些字符与模式串开头位置的字符一样避免这些位置的重复比较的.例如 主串: abcabcabcabed ,模式串:abcabed.当比较到模式串'e'字符时不同的时候完全没有必要从模式串开始位

  • Java实现ECDSA签名算法

    ECDSA签名算法 package com.albedo.security; /** * DSA 加解密实现 */ public class ECDSAUtils extends Base { //字符编码 public static final String ALGORITHM = "EC"; public static final String SIGN_ALGORITHM = "SHA1withECDSA"; /** * ECDSA 验签 * * @param

  • java括号匹配算法求解(用栈实现)

    如何使用栈来判定括号是否匹配 对于给定的表达式,可以使用栈来实现括号匹配判定,这个算法在编译器中非常重要,解析器每次读入 一个字符,如果字符是一个开分隔符,如(,[,{,入栈,若读入的是闭分隔符),],},出栈,如果两者匹配,继续解析字符串,如果不匹配,解析器错误 算法思路 1.创建一个栈 2.当(当前字符不等于输入的结束字符) (1)如果当前字符不是匹配的字符,判断栈内是否为空,如果栈为空,括号必然不完整 (2)如果字符是一个开分隔符,那么将其入栈 (3)如果字符是一个闭分隔符,,且栈不为空,

随机推荐