java算法篇KMP算法及应用
发布日期:2021-06-30 16:19:32 浏览次数:2 分类:技术文章

本文共 2438 字,大约阅读时间需要 8 分钟。

算法背景

给定两个字符串,判断是否一个字符串包含另外一个字符串,如果包含,返回起始位置。比如:

String  str1=“abceacmk32acmzq”

String str2=“acm”

可以看出,str1包含两处str2,下面红色地方:

abceacmk32acmzq

返回4和10.

常见思路1

遍历str1,先匹配第一个,如果不相同,跳过,继续寻找,如果相同,截取和str2相同长度的子串,比较是否相同,如果相同,则返回,如果不同继续寻找,一直到str1到最后一个字符。代码如下

package com.puhui.goosecard.web;class GFG {    public static void main(String[] args) {        String str1 = "abceacmk32acmzq";        char[] chars1 = str1.toCharArray();        String str2 = "acm";        char[] chars2 = str2.toCharArray();        for (int i = 0; i < chars1.length; i++) {            if (chars1[i] != chars2[0]) {                continue;            }            String temp = str1.substring(i, i + str2.length());            if (temp.equalsIgnoreCase(str2)) {                System.out.print(i);            }        }    }}

输出结果:

4

10

时间复杂度:

常见思路2

思路一利用了jdk的接口,如果不用呢?思路二:

  • 设置两个指针i和j从头开始遍历两个字符串
  • 当两个指针都小于字符串长度时,循环后移,比较当前元素,如果相同,继续后移,如果不相等,j重置为0,i从下一个元素开始,如果第一个是从0开始,那么下一个就是1,在程序中是i-j+1,开始,一定注意条件,如果不相同的情况。
  • 直到j等于查询字符长度,输入当前位置,为i-char2.length+1
  • 完成上面逻辑之后,无法匹配下一个重复的子串,所以,如果满足条件3之后,重置j=0;
package com.puhui.goosecard.web;class GFG {    public static void main(String[] args) {        String str1 = "abceacmk32acmzq";        char[] chars1 = str1.toCharArray();        String str2 = "acm";        char[] chars2 = str2.toCharArray();        /**从头的指针这么写*/        int i = 0;        int j = 0;        while (i < chars1.length && j < chars2.length) {            if (chars1[i] == chars2[j]) {                i++;                j++;            } else {                i = i - j + 1;                j = 0;            }            if (j == chars2.length - 1) {                //多个重复,重新开始寻找,j重置,i不变                j = 0;                System.out.println(i - chars2.length + 1);            }        }    }}

KMP

建立部分匹配表

思路二已经解决了我们的问题,但是是否可以优化呢?当然可以,我们发现如果不匹配,是否可以有效的移动j,而不是回溯到0?

所以,整个KMP的重点就在于当某一个字符与主串不匹配时,我们应该知道j指针要移动到哪利用先前比较的结果跳过一些比较

先看一下思路,先建立一个部分匹配table

m[i]的值就是,小于i的最长后缀。

比如 p1元素是a,最长后缀是a,i=1,所以m[i]的值为0.

比如p4为abab,最长后缀为ab且满足2小于i(4)

比如p6为ababab,最长后缀且满足小于6的是abab,也就是4

比如p9为ababababc,最长后缀且满足小于9的是0,因小于等于8的里面没有c

we look at all the prefixes of P that are suffixes of P1 . . . Pi−1, and find the longest one whose next letter matches Pi

求出P0···Pi的最大相同前后缀长度k

举例

T=ABC ABCDAB ABCDABCDABCD

P=ABCDABD

下面是我的计算过程

有个这个映射关系,怎么用,推到过程比较复杂,详细看下面的连接,

结果就是

p的移动位数=k-m[k],其中k为当前匹配的个数,m[k]为我们上面推到的结果,也就是(0,0,0,0,1,2,0)

优化原算法

关键点:如果发现不匹配,只能不用回溯到下一个位置,利用已经匹配到元素判断移动位置:

移动位数 = 已匹配的字符数 - 最后一个匹配字符对应的部分匹配值

代码实现

 

转载地址:https://kerry.blog.csdn.net/article/details/82803096 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:LeetCode刷题Easy篇在整型数组寻找两个数的和等于指定数
下一篇:Java框架篇spring sleuth分布式日志查询

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月21日 03时38分27秒