Sunday算法是用于查找子串的一种算法,具体的应用场景看这里
相比于KMP,BM算法,Sunday算法更快,并且更好理解(自认为哈
下面用比较正式的语言介绍一下该算法:
原字符串为文本串,匹配的子串为模式串。
从前往后匹配,在匹配失败时,关注的是文本串中参加匹配的最末位字符的下一位字符。
—如果该字符没有在模式串中出现则直接跳过,即移动位数 = 匹配串长度 + 1;
—否则,其移动位数 = 模式串中最右端的该字符到末尾的距离+1。
是不是看的一脑袋问号?事实上在学习一个算法时,我认为不应该一开始就看概念,而是从例子入手。读懂了例子,就有事半功倍的效果。
话不多说,举一个栗子:
令文本串为:"substring searching algorithm
令模式串为:search
.
1.初始状态:
现在匹配失败了,那么我们要试图移动模式串,再进行下一次匹配
(事实上我觉得各个算法不同的地方就在于怎么移动模式串)
Sunday算法选择关注文本串中参加匹配的最末位字符的下一位字符
在这里,就是箭头指向的 i 。可以看到,i 没有出现在模式串search中。
所以,我们可以说,文本串中所有包括i的部分,都不会与search匹配。
2.移动模式串
这样我们可以直接将search直接移动到 i 的后面。从i后面的元素n开始匹配。
ok,现在模式串search还是没有与文本串匹配。我们继续关注上文所说的“下一位字符”,也就是箭头所指的 r。
r出现在了模式串中,出现在模式串的倒数第3位。
那么我们将模式串向右移动3位。也就是将两个r先匹配起来.
3.移动模式串
这里,匹配成功了。完结撒花~~
现在,回头看看该算法的定义,会不会豁然开朗了呢?
接下来就上代码了~
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
def calShiftMat(st):
dic = {}
for i in range(len(st)-1,-1,-1):
if not dic.get(st[i]):
dic[st[i]] = len(st)-i
dic["ot"] = len(st)+1
return dic
# 关于这个函数,我觉得就是为了降低时间复杂度的
# 之前我是用in和index来判断“下一位字符”以及移动的距离。
# 但leetcode过不了..
if not needle:
return 0
len_a = len(haystack)
len_b = len(needle)
dic = calShiftMat(needle)
start = 0
while start <= len_a - len_b:
if haystack[start:start+len_b] == needle:
return start # 如果匹配,直接返回起始位置
else :
if start+len_b >= len_a:
return -1
cur_c = haystack[start+len(needle)]
if dic.get(cur_c):
start += dic[cur_c]
else:
start += dic["ot"]
return -1 if start+len(needle) >= len(haystack) else start
.
参考资料
- https://blog.csdn.net/v_july_v/article/details/7041827#
- https://leetcode-cn.com/problems/implement-strstr/solution/python3-sundayjie-fa-9996-by-tes/