KMP算法

1 用途

主要用于在文本串中查找模式串是否存在,并返回模式串开始的索引。
文本串长度为n,模式串长度为吗,正常一个个字符比对,匹配失败时文本串指针向后移动一个字符,模式串指针移动到开头,这样来查找模式串的时间复杂度为O(m*n)。
KMP方法降低了查找的时间复杂度。

2 Leetcode对应题目

https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string
KMP算法_第1张图片

3 主要思路

在每次匹配失败时,模式串指针不是移动到模式串的开头,而是移动到和当前已经匹配过的模式串的后缀对应的前缀的位置。

KMP算法_第2张图片
即b和f不匹配的时候,不像暴力算法这样比较。
KMP算法_第3张图片
而是这样比较。
KMP算法_第4张图片
可以看到,文本串的指针向后移动了一位,而模式串的指针从f回退到了b,那是如何回退到b的呢,我们接下来详细说说。

4 KMP详细思路

先来说说是如何回退到b的,是因为f前面的子串aabaa这个字符串的相等的最长前缀和最长后缀为aa,即最开始的两个aa和最后的两个aa。因此当在模式串的字符f匹配失败时,由于之前aabaa已经匹配成功了,而且aabaa这个子串的最长的相等前后缀为aa,长度为2,因此aabaa的前两个字符在新一轮的匹配中是不需要重新匹配的,可以直接从b位置开始匹配。

完整的步骤是这样的:
一开始需要计算模式串每个从开头开始的子串的最长相等前后缀的长度。便于匹配失败时,模式串指针的跳转。
模式串指针和文本串指针都从0开始匹配。
当两个指针指向的字符相等时,就同时向后移动。
当两个指针指向的字符不相等时,不断尝试跳转模式串的指针,直到匹配成功或者跳转到模式串的开始位置。文本串的指针向后移动1。
循环结束后,当模式串指针指向模式串的末尾,证明匹配成功。否则匹配失败。

5 实现代码

class Solution:
    def get_next(self, s):
        res=[0]
        for i in range(1, len(s)):
            l,r=0,i
            tmp_res=0
            while l<i:
                if s[0:l+1]==s[r:i+1]:
                    tmp_res=l+1
                l+=1
                r-=1                    
            res.append(tmp_res)
        # print(res)
        return res
    def strStr(self, haystack: str, needle: str) -> int:
        s,p=0,0
        p_next=self.get_next(needle)
        while s<len(haystack) and p<len(needle):
            while p>0 and haystack[s]!=needle[p]:
                p=p_next[p-1]
            if haystack[s]==needle[p]:
                s+=1
                p+=1
            else:
                s+=1
            # print(f"s:{s} p:{p}")
        if p==len(needle):
            return s-len(needle)
        else:
            return -1

你可能感兴趣的:(算法,算法,leetcode)