https://www.bilibili.com/video/BV1S64y1u74P?from=search&seid=5815335614123775779
kmp算法概述
小串匹配大串,一下子能想起来的思路是暴力匹配,对两个串循环,大串从前往后循环,小串去匹配,当匹配不上的时候,小串index退回到0的位置。这种暴力法复杂度高。
kmp是在小串回退的时候做了优化:
回退到后缀的位置,而不是回退到首位。
后缀根据next表得到。就是当前字符前面的,前后一样的连续字符的最大长度。
考虑边界条件,设置首位为-1,这样就不会死循环。
代码实现
https://www.cnblogs.com/sfencs-hcy/p/10646437.html
分两个部分,1个是主函数,1个是前缀表的实现。
# -*- coding: utf-8 -*-
"""
Created on Sun Sep 6 11:33:40 2020
@author: Asus
"""
def same_start_end(s):
"""最长前后缀相同的字符位数"""
n = len(s) #整个字符串长度
j = 0 # 前缀匹配指向
i = 1 # 后缀匹配指向
result_list=[0]*n
while i < n:
if j == 0 and s[j] != s[i]: # 比较不相等并且此时比较的已经是第一个字符了
result_list[i] = 0 # 值为0
i += 1 # 向后移动
elif s[j] != s[i] and j != 0: #比较不相等,将j值设置为j前一位的result_list中的值,为了在之前匹配到的子串中找到最长相同前后缀
#递归的思想,看看能不能在前面的子串中找到匹配的位置。最坏的情况是回溯到j=0
j = result_list[j-1]
elif s[j] == s[i]: #相等则继续比较
#如果s[i]与s[j]相等,则前缀后缀的长度为前缀的位置(因为前缀是从0开始)
result_list[i] = j+1
j = j+1
i = i+1
return result_list
def kmp(s,p):
"""kmp算法,s是字符串,p是模式字符串,返回值为匹配到的第一个字符串的第一个字符的索引,没匹配到返回-1"""
s_length = len(s)
p_length = len(p)
i = 0 # 指向s
j = 0 # 指向p
next_table = same_start_end(p)
while i < s_length:
if s[i] == p[j]: # 对应字符相同
i += 1
j += 1
if j >= p_length: # 完全匹配
return i-p_length
elif s[i] != p[j]: # 不相同
if j == 0: # 与模式比较的是模式的第一个字符
i += 1
else: # 取模式当前字符之前最长相同前后缀的前缀的后一个字符继续比较
j = next_table[j]
if i == s_length: # 没有找到完全匹配的子串
return -1
s="望江楼上望江江流"
res=same_start_end(s)
print(res)