代码随想录训练营Day9|KMP字符串匹配

2023-10-19  本文已影响0人  是小张啊啊

KMP算法基础

举例:字符串 "aabaaf"
详细过程:
i = 0, 字符串为 a,最长相同前后缀长度为 0
i = 1, 字符串为 aa,前缀:a,后缀:a,存在1个相同的前后缀,则最长相同前后缀长度为 1
i = 2, 字符串为 aab,前缀:a,aa,后缀:b,ab,不存在相同的前后缀,则最长相同前后缀长度为 0
i = 3, 字符串为 aaba,前缀:a,aa,aab,后缀:a,ba,aba,存在 1 个相同的前后缀 a,则最长相同前后缀长度为 1
i = 4, 字符串为 aabaa,前缀:a,aa,aab,aaba,后缀:a,aa,baa,abaa,存在 2 个相同的前后缀 a 和 aa,则最长相同前后缀为 2
i = 5, 字符串为 aabaaf,前缀:a,aa,aab,aaba,aabaa,后缀:f,af,aaf,baaf,abaaf,存在 0 个相同的前后缀,则最长相同前后缀为 0
所以其前缀表为 [0, 1, 0, 1, 2, 0]

举例:文本串"aabaabaafa",模式串"aabaaf"


image.png

如图,当匹配到下标 i = 5时,当出现 b 和 f 不匹配的字符时,下一步可以直接跳到下标为 2 的位置继续匹配。
至于为什么是跳转到下标为2的位置,是根据前缀表来的
下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa ,因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面重新匹配就可以了。

  1. 初始化两个指针 i = 0, j = -1,数组 next = [j];
    其中 i 指向后缀末尾位置,j 指向前缀末尾位置;
    比如i = 2,指向字符b,已经匹配过的字符串为 aab,此时 i 指向后缀末尾即字符b,j 指向前缀末尾即字符a;
  2. 处理前后缀不相同的情况
  3. 处理前后缀相同的情况

28. 找出字符串中第一个匹配项的下标

解题思路
var strStr = function(haystack, needle) {
    if (needle.length === 0) {
        return 0;
    }
    // 初始化j=-1
    let j = -1;
    let next = []
    next.push(j);
    // 遍历模式字符串
    for (let i = 1; i < needle.length; i++) {
        // 前后缀不相同
        while(j >= 0 && needle[i] !== needle[j+1]) {
            j = next[j] // 向前回退
        }
        // 前后缀相同
        if (needle[i] === needle[j+1]) {
            j++
        }
        next.push(j)
    }
    j = -1;
    for (let i = 0; i < haystack.length; i++) {
        while(j >= 0 && haystack[i] !== needle[j+1]) {
            j = next[j]
        }
        if (haystack[i] === needle[j+1]) {
            j++
        }
        if (j == (needle.length - 1) ) { // 文本串s里出现了模式串t
            return (i - needle.length + 1);
        }
    }
    return -1
};
上一篇 下一篇

猜你喜欢

热点阅读