动画演示Sunday字符串匹配算法——比KMP算法快七倍!极易理解!
前言
上一篇我用动画的方式向大家详细说明了KMP算法(没看过的同学可以回去看看)。
这次我依旧采用动画的方式向大家介绍另一个你用一次就会爱上的字符串匹配算法:Sunday算法,希望能收获你的点赞关注收藏与转发哟!
KMP算法是一个里程碑似的算法,它的出现宣告了人类是找到线性时间复杂度的字符串匹配算法的。在这之后,出现了许多的字符串匹配算法,比如BM算法和Sunday算法。
这些算法在时间复杂度上都已经达到了线性时间。但是在实际应用的时候所耗费的时间却还是有所不同。
BM算法在实际应用中的效率已经达到了KMP算法的四五倍。
而Sunday算法的效率甚至犹在BM算法之上。
并且若是两种算法都了解的同学会明白:
Sunday算法比起BM算法来,真的极其容易理解。
正文
行,咱对Sunday算法的吹捧先到这为止,下面开始正戏!
PS:以下将带匹配字符串称为文本串,将用来匹配的字符串称为模式串。
为什么说Sunday算法极易理解呢?
因为它比暴力匹配算法只多了一个步骤而已!
话不多说,直接上我精心制作的GIF动态图:

可以看到,我们只移动了三次,就直接找到了最终的结果。
Sunday算法是从前往后匹配的算法(BM算法是从后向前的),在匹配失败时重点关注的是文本串中参加匹配的最末位字符的下一位字符。
如果该字符没有在模式串中出现则直接跳过,即移动位数 = 模式串长度 + 1。
否则,其移动位数 = 模式串长度 - 该字符最右出现的位置(以0开始) = 模式串中该字符最右出现的位置到尾部的距离 + 1。
Sunday算法最巧妙的地方,就在于它发现匹配失败之后可以直接考察文本串中参加匹配的最末尾字符的下一个字符。
在python代码中,我们利用字典来存储模式串中每个字符最后出现的索引,这样在前期只需O(M),M为模式串长度的时间即可做完前期准备,然后再进行查询都是O(1)的时间。
同时为了防止越界,我在下面贴出来的python代码中手动在字符串末尾加上了一个'\0'字符。
代码
class Sunday(object):
def __init__(self, pattern:str):
# 模式串和其长度
self.pattern, self.length = pattern, len(pattern)
# 根据模式串构建的偏移字典
self.shift_dict = {}
# 构建字典
for index, value in enumerate(pattern):
self.shift_dict[value] = self.length - index
def match(self, text:str):
i = 0
text_length = len(text)
text += '\0'
while i <= text_length - self.length:
j = 0
while self.pattern[j] == text[i + j]:
j += 1
if j >= self.length:
return i
offset = self.shift_dict[text[i+self.length]] if text[i+self.length] in self.shift_dict else self.length + 1
i += offset
return -1
s = Sunday('nihao')
print(s.match('dasoijfoasjdoifjasdoifjoinihao'))
代码十分的简单,同时,我构造了一个类,是为了在同一个模式串下能够复用它的位置字典,简化代码。
Sunday算法与KMP算法大比拼
在写完代码之后,我对KMP算法和Sunday算法的匹配时间进行了一个粗略的检测,检测结果如下:

amazing!,Sunday算法的平均匹配速度达到了KMP算法的七倍左右!
对KMP和Sunday各自构造了一个对象,然后每次生成一个随机的十万个字符长度的字符串让它们俩分别开始匹配。
生成-->匹配这个过程循环一百遍,最终计算平均时间。如果有大佬觉得不放心的,我在下方放出检测代码,大家可以自行修改测试,拿去即可用!
检测代码如下
class KMP():
def __init__(self, ss: str) -> list:
self.length = len(ss)
self.next_lst = [0 for _ in range(self.length)]
self.next_lst[0] = -1
i = 0
j = -1
while i < self.length - 1:
if j == -1 or ss[i] == ss[j]:
i += 1
j += 1
if ss[i] == ss[j]:
self.next_lst[i] = self.next_lst[j]
else:
self.next_lst[i] = j
else:
j = self.next_lst[j]
self.pattern = ss
def match(self, ss:str):
ans_lst = []
j = 0
for i in range(len(ss)):
if ss[i] != self.pattern[j]:
j = self.next_lst[j] if self.next_lst[j] != -1 else 0
if ss[i] == self.pattern[j]:
j += 1
if j == self.length:
return i + 1 - self.length
return -1
class Sunday(object):
def __init__(self, pattern:str):
# 模式串和其长度
self.pattern, self.length = pattern, len(pattern)
# 根据模式串构建的偏移字典
self.shift_dict = {}
# 构建字典
for index, value in enumerate(pattern):
self.shift_dict[value] = self.length - index
def match(self, text:str):
i = 0
text_length = len(text)
text += '\0'
while i <= text_length - self.length:
j = 0
while self.pattern[j] == text[i + j]:
j += 1
if j >= self.length:
return i
offset = self.shift_dict[text[i+self.length]] if text[i+self.length] in self.shift_dict else self.length + 1
i += offset
return -1
import random
import time
sunday = Sunday('helloworld')
kmp = KMP('helloworld')
kmp_average_time = 0
sunday_average_time = 0
for i in range(100):
ss = ''.join([chr(random.randint(97, 122)) for _ in range(100000)])
st = time.process_time()
sunday.match(ss)
ed = time.process_time()
sunday_average_time += ed - st
st = time.process_time()
kmp.match(ss)
ed = time.process_time()
kmp_average_time += ed - st
print('kmp平均时间: {}'.format(kmp_average_time / 100))
print('sunday平均时间: {}'.format(sunday_average_time / 100))
最后
最后,如果你觉得这篇文章对你有帮助的话呢,给我点个关注,收藏吧!
我的个人公众号是【程序小员】,欢迎你的关注,你的认可是我最大的动力!
我会持续更新对你有帮助的文章!
我是落阳,谢谢你的到访!
动画演示Sunday字符串匹配算法——比KMP算法快七倍!极易理解!的更多相关文章
- 字符串匹配算法之 kmp算法 (python版)
字符串匹配算法之 kmp算法 (python版) 1.什么是KMP算法 KMP是三位大牛:D.E.Knuth.J.H.MorriT和V.R.Pratt同时发现的.其中第一位就是<计算机程序设计艺 ...
- Python 细聊从暴力(BF)字符串匹配算法到 KMP 算法之间的精妙变化
1. 字符串匹配算法 所谓字符串匹配算法,简单地说就是在一个目标字符串中查找是否存在另一个模式字符串.如在字符串 "ABCDEFG" 中查找是否存在 "EF" ...
- 字符串匹配算法之————KMP算法
上一篇中讲到暴力法字符串匹配算法,但是暴力法明显存在这样一个问题:一次只移动一个字符.但实际上,针对不同的匹配情况,每次移动的间隔可以更大,没有必要每次只是移动一位: 关于KMP算法的描述,推荐一篇博 ...
- 字符串匹配算法之kmp算法
kmp算法是一种效率非常高的字符串匹配算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,所以简称KMP算法 算法思想 在一个字符串中查找另一个字符串时,会遇到如下图的情况 我们通常 ...
- 字符串匹配算法(三)-KMP算法
今天我们来聊一下字符串匹配算法里最著名的算法-KMP算法,KMP算法的全称是 Knuth Morris Pratt 算法,是根据三位作者(D.E.Knuth,J.H.Morris 和 V.R.Prat ...
- 数据结构学习之字符串匹配算法(BF||KMP)
数据结构学习之字符串匹配算法(BF||KMP) 0x1 实验目的 通过实验深入了解字符串常用的匹配算法(BF暴力匹配.KMP.优化KMP算法)思想. 0x2 实验要求 编写出BF暴力匹配.KM ...
- 字符串匹配算法之BM算法
BM算法,全称是Boyer-Moore算法,1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹配算法. BM算法定义了两个规则: ...
- 数据结构4_java---顺序串,字符串匹配算法(BF算法,KMP算法)
1.顺序串 实现的操作有: 构造串 判断空串 返回串的长度 返回位序号为i的字符 将串的长度扩充为newCapacity 返回从begin到end-1的子串 在第i个字符之前插入字串str 删除子串 ...
- Sunday字符串匹配算法
逛ACM神犇的博客的时候看到的这个神奇的算法 KMP吧,失配函数难理解,代码量长 BF吧,慢,很慢,特别慢. BM吧,我不会写... 现在看到了Sunday算法呀,眼前一亮,神清气爽啊. 字符串匹配算 ...
随机推荐
- leetcode刷题-73矩阵置零
题目 给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0.请使用原地算法. 示例 1: 输入: [ [1,1,1], [1,0,1], [1,1,1]]输出: ...
- leetcode刷题-50Pow(x, n)
题目 实现 pow(x, n) ,即计算 x 的 n 次幂函数. 思路 最初的想法n>0计算res = res*x 计算n次,n<0,将x取倒数后同理,但结果表明计算速度太慢了. 后续应该 ...
- path.resolve和path.join的区别
// test.js const path = require('path') let x1 = path.resolve('/目录1/目录2', '/目录3/目录4/') let x2 = path ...
- C#开发PACS医学影像处理系统(六):加载Dicom影像
对于一款软件的扩展性和维护性来说,上层业务逻辑和UI表现一定要自己开发才有控制权,否则项目上线之后容易被掣肘, 而底层图像处理,我们不需要重复造轮子,这里推荐使用fo-dicom,同样基于Dicom3 ...
- Linux高级命令进阶
输出重定向 场景:一般命令的输出都会显示在终端中,有些时候需要将一些命令的执行结果想要保存到文件中进行后续的分析/统计,则这时候需要使用到的输出重定向技术. >:覆盖输出,会覆盖掉原先的文件内容 ...
- openstack核心组件——keystone身份认证部署服务(5)
node1主机执行 1.mysql -u root -p 2.create database keystone; 创建数据库 MariaDB [(none)]> show databases; ...
- web网站——理论01
LAMP的架构以及通信过程 LAMP的架构: LAMP是一个多C/S架构的平台,最初级为web客户端基于TCP/IP通过http协议发起传送,这个请求可能是动态的,也可能是静态的. 所以web服务器通 ...
- 项目初始化CSS公共样式
/*! normalize.css v6.0.0 | MIT License | github.com/necolas/normalize.css */ /* Document =========== ...
- spring boot 源码之SpringApplicationRunListeners
SpringApplicationRunListeners SpringApplicationRunListener的集合,内部存储了SpringApplicationRunListener的集合,提 ...
- 2.2 spring5源码 -- ioc加载的整体流程
之前我们知道了spring ioc的加载过程, 具体如下图. 下面我们就来对照下图, 看看ioc加载的源代码. 下面在用装修类比, 看看个个组件都是怎么工作的. 接下来是源码分析的整体结构图. 对照上 ...