栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

KMP算法——解决字符串匹配问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

KMP算法——解决字符串匹配问题

哪里需要

leedcood 28:实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回  -1 。

示例 1:

输入:haystack = "hello", needle = "ll"
输出:2
示例 2:

输入:haystack = "aaaaa", needle = "bba"
输出:-1

核心思想

字符串在比较失败时,已经知道之前主串和子串都读过哪些字符,避免主串跳回下一个字符重新匹配的步骤(这也是暴力解法的思路):

  暴力解法思路

KMP算法关键:在当前主串和字串检索过程中,发现不匹配(如主串的A和子串的C),主串不退回,子串都过某种条件移动到与主串A相等的字符上去,再重新比较匹配,如下图

换句话说,在一次遍历主串的过程中找出匹配的子串

某种条件是什么?

这里就需要KMP中定义的next数组,我们先来看怎么利用next数组

KMP算法在匹配失败时,会去看最后一个匹配的字符(例子中的B)所对应的next数值

 此时为2,于是我们移动字串,跳过字串前两个字符

 这里的2代替字串中我们可以跳过匹配的字符个数,即重新开始匹配的子串下标也为2

 重新开始匹配,当子串指针到达末尾,就匹配成功

算法实现(Java)
 public int strStr(String haystack, String needle) {
        
        //得到next数组
        int[] next = new int[needle.length()];
        getNext(next, needle);

        int j = 0; //子串指针
        for (int i = 0; i < haystack.length(); i++) {
            while (j > 0 && needle.charAt(j) != haystack.charAt(i)) 
                j = next[j - 1];  //最后一个匹配的字符所对应的next数值=重新开始匹配的子串下标
            if (needle.charAt(j) == haystack.charAt(i)) 
                j++;
            if (j == needle.length()) 
                return i - needle.length() + 1;
        }
        return -1;

    }

next数组的实现

 那么子串的 ABABC 中 0 0 1 2 0 是如何算出来的?

首先了解一下前缀和后缀的概念

前缀:不包含尾字母的所有字串

以ABABC为例子

前缀有:

A

AB

ABA

ABAB

后缀:不包含头字母的所有字串

以ABABC为例子

后缀有:

B

BA

BAB

BABC

最长相等前后缀:前缀和后缀中最长相等的,以ABABC为例子,子串有五种情况如下

A:没有前缀,也没有后缀,最长相等前后缀0

AB:前缀有A,后缀有B,最长相等前后缀0

ABA:前缀有A,AB,后缀有A,BA,最长相等前后缀1

ABAB:前缀有A,AB,BA,ABA,后缀有B,BA,AB,BAB,最长相等前后缀2

ABABC:前缀A,AB,ABA,ABAB,后缀B,BA,BAB,BABC,最长相等前后缀0

最长相等前后缀 A B A B C 0 0 1 2 0

 

算法实现(Java)

动态规划思想:假设我们已经知道当前共同前后缀,如果下一个字符相等,则最长共同前后缀+1

如果不相等,查看是否有更小共同前后缀,利用左边的最后一个相等下标变成当前长度,重新匹配

 

private void getNext(int[] next, String s) {
        int j = 0; //最长相等前后缀,数组的值
        next[0] = 0;  //开始为0
        for (int i = 1; i < s.length(); i++) {
            while (j > 0 && s.charAt(j) != s.charAt(i)) //如果不相等,查看更小共同前后缀 
                j = next[j - 1];
            if (s.charAt(j) == s.charAt(i))  //如果下一个字符相等,则最长共同前后缀+1
                j++;
            next[i] = j; 
        }
    }

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1003440.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号