【KMP算法原理及实战】

题目

实现 strStr() 函数。

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

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例 1:

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

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

来源:力扣(LeetCode)
链接:原题链接
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

KMP算法简介

算法背景

在一个长字符串中,查找是否有一字串出现。暴力匹配是这样的:从头开始匹配,出现不匹配的字符在从第二个字符开始看是否匹配。这样的时间复杂度随着是极高的。

kmp的思想

当出现不匹配的情况时,如何快速定位到下次匹配开始的位置,这很关键。kmp利用了前缀表来解决定位的问题

前缀表,后缀表

前缀表就是不包含最后一个字符的所有子字符串,例如:
对于字符串 “aaadaa”,它的所有前后缀字符串分别为:
前缀: 后缀:
a, a
aa, aa
daa, aaa
adaa, aaad
aadaa, aaada

next数组(数组元素值全做减 1 处理)

next数组的元素是匹配失败后,定位的位置下标。next数组的元素怎么来的呢?
假设当前的子字符串是 needle ,那么从前向后扫描needle的所有子字符串,比如“aaadaa”,
第一个子字符串为 a ,此时没有前后缀字符串,所以前后缀相等长度为 0,所以当前值就是数组的第一个数,设置为 0 - 1 = -1;
第二个子字符串为 aa,此时前缀字符串为 a ,后缀字符串也为 a ,且相等,相等的长度为1,那么存到数据就是 1 - 1 = 0;
以此类推,next数组存的元素就是前后缀相等的前缀长度,也等于后缀长度。所以这个next数组可以也叫做前缀数组。

算法原理

将要匹配的模式字符串转为next数组,由于next数组保存的是前后缀相等的模式字符串的子字符串的前缀长度(减 1 ),所以当模式字符串匹配到半路发生不匹配时,定位的操作就交给这个next数组,假设在目标字符串的下标为5的位置发生了不匹配,那就查next数组中下标为4的值,将匹配初始位置回退到这个值。这个原因就是,next数组存储了前后缀相等的前后缀长度,并且不匹配位置之前一定都是匹配的,那么就可以用当前子字符串的前缀取匹配当前的后缀位置,这是一定匹配的,而且可以快速跳过不必要的对比,时间复杂度为O(n + m)

代码


public class KMPController {
    public static void main(String[] args) {
        int str = strStr("hello", "ll");
        System.out.println(str);//输出为2
    }

    public static void getNext(int[] next, String s) {
        int j = -1;
        next[0] = j;
        for(int i = 1; i < s.length(); i++) {
            //i和j的元素不等时,重置j的起点为next数组最新元素
            if(j >= 0 && s.charAt(i) != s.charAt(j + 1)) {
                j = next[j];
            }
            if(s.charAt(i) == s.charAt(j + 1)) {
                j++;
            }
            next[i] = j;
        }
    }

    public static int strStr(String haystack, String needle) {
        if(needle.length() == 0) {
            return 0;
        }
        int[] next = new int[needle.length()];
        getNext(next, needle);
        int j = -1;
        for (int i = 0; i < haystack.length(); i++) {
            if(j >= 0 && haystack.charAt(i) != needle.charAt(j + 1)) {
                j = next[j];
            }
            if (haystack.charAt(i) == needle.charAt(j + 1)) {
                j++;
            }
            if (j == needle.length() - 1) {
                return (i - needle.length() + 1);
            }
        }
        return -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值