28. 实现 strStr()
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
本方法就是最原始的从头开始匹配,如果匹配错误则接着下一个点继续匹配。综合看来不是很好。
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
hay_count = len(haystack)
p_count = len(needle)
if p_count == 0:
return 0
i,j = 0,0
while i < hay_count:
if haystack[i] == needle[j]:
j += 1
i += 1
else:
i -= (j-1)
j = 0
if j == p_count:
return i-j
return -1
相似的查找算法有 KMP,BM,Horspool,之后有空在学一下这几种方法。
看了两天KMP算法,不得不说这个算法真的挺巧妙的。首先看下源码
def KMP_algorithm(string, substring):
'''
KMP字符串匹配的主函数
若存在字串返回字串在字符串中开始的位置下标,或者返回-1
'''
pnext = gen_pnext(substring)
n = len(string)
m = len(substring)
i, j = 0, 0
while (i<n) and (j<m):
if (string[i]==substring[j]):#字符相匹配,两个游标都后移
i += 1
j += 1
elif (j!=0):
j = pnext[j-1]
else:
i += 1
if (j == m):#这里的如果匹配完毕,就返回匹配的首地址
return i-j
else:
return -1
def gen_pnext(substring):
"""
构造临时数组pnext
"""
index, m = 0, len(substring)
pnext = [0]*m #创建一个等长的0列表
i = 1
while i < m:
if (substring[i] == substring[index]):#字符相匹配,两个游标都后移
pnext[i] = index + 1 #pnext[i] 中保存位置i之前最大的匹配个数
index += 1
i += 1
elif (index!=0):#如果不匹配,则回溯游标,回溯到前一个index在pnext中的位置
index = pnext[index-1]
else:
pnext[i] = 0#这种情况即index == 0,并且两者还不匹配
i += 1#直接去匹配下一个
return pnext
if __name__ == "__main__":
string = 'abcxabcdabcdabcy'
substring = 'abcdabcy'
out = KMP_algorithm(string, substring)
print(out)
算法有点难理解 ~_~
i 为原始的列表中的游标
index 为子模板的列表中的游标
pnext[ i ] 为第 i 位和之前的字串中 匹配成功的前缀和后缀的长度
首先分为三种情况:
- s[ index ] == s[ i ]: 表示子模板中的元数与原始列表中的元素匹配上了。这个时候在 pnext[i] = index + 1,保存第i个和其之前最长的匹配前后缀个数。
- 匹配不上,并且index不等于0,这意味着,在这之前是有匹配成功的,就是在这里可能会断掉。这时候就需要回溯index到一个适合的位置。这个位置的确定有点迷。首先例如: abbabbabbac,我们对齐创建pnext数组开始的匹配比较清楚 pnext = [0001234567*],在最后一位的时候 index = 7, i = 10,两者不匹配。此时的 pnext 列表内的值如下
代码里面是回溯的 index = pnext [ index - 1 ],即 pnext[6] = 4,index = 4,向前移动了三位,也就是匹配前重复的数组长度3,list内有3个0, 如果匹配的长度超过3,那么 s[i]之后的值是和s[3]会重复的。因为只有重复匹配的长度才会超过3,所以这个地方回溯的移动值就是列表前面重复的长度。 - 第三种情况就是index = 0, 但是 s[index] != s[ i ], 表示两个串的串首都不相等,后移 i 。
上述就是pnext列表的生成,有点不好理解,目前我自己也没有完全理解,惭愧~