LeetCode-53 实现strStr()
LeetCode-53 实现strStr()
1. 题目
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
2. 朴素匹配
暴力匹配算法,从头开始一个字符一个字符匹配,时间复杂度为 $ O(m*n) $
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
h_len=len(haystack)
n_len=len(needle)
if n_len>h_len:
return -1
if n_len==0 or (n_len==h_len and haystack==needle):
return 0
for i in range(h_len):
if haystack[i]==needle[0]:
k=i
j=0
if n_len<=h_len-i:
while j<n_len:
if needle[j]!=haystack[k]:
break
j+=1
k+=1
if j==n_len:
return k-n_len
return -1
3. KMP算法
- next数组构建
KMP算法的时间复杂度为 \(O(m+n)\)
KMP算法的核心在于next数组的构建,next数组中,next[i]代表的是最长的公共前缀后缀长,例如对如字符串"ababc",
首先前缀是指不包含最后一个值的字符串,后缀指不含第一个值的字符串
i=0时,a最长的前后缀为'',next[0]=0
i=1时,ab最长的前后缀为'',next[1]=0
i=2时,aba的最长前后缀为'a',next[2]=1
i=3时,abab的最长前后缀为'ab',next[2]=2
i=3时,ababc的最长前后缀为'',next[2]=0
要推断字符串的最长前后缀长度的复杂度是 $ O(n^2) $ ,但在构建的过程中是有规律的
对于ababc:
初始化:next=[0]*len
第一个字符的next值为0
接着遍历后面的字符
用k来代表当前最长前后缀的起始位置
此时i=1,k=0
对于字符b,此时k=0,表示当前最大前后缀的起始位置为0,判断a!=b,所以next[1]=0,并将i后移
此时i=2,k=0
对于字符a,因为a(i=2)a(k=0),代表有相同的前后缀,将k后移,且next[2]=1,并将i,k后移
此时i=3,k=1
对于字符b,因为a(i=3)a(k=1),代表有相同的前后缀,将k后移,且next[3]=2,并将i,k后移
此时i=4,k=2
对于字符c,因为a(i=4)!=a(k=2),没有相同的前后缀,此时k退回next[k-1],next[k-1]表示k前面一个位置的最长前后缀的长,k=next[k-1]=next[1]=0,当k=0 or s[i]s[k]时,循环结束,如果此时k0 and s[i]!=s[k],则该位置最长前后缀为0,否则为next[k]
- 匹配过程
在第三个字符匹配错误时,KMP算法比朴素算法的优点就体现出来了,朴素匹配算法会直接进入下一个字符,然后从头开始
KMP算法会根据next数组,直接选择有公共前后缀的部分,跳过已经匹配过的部分,只经过一次遍历即可匹配
- 代码
class Solution: def strStr(self, haystack: str, needle: str) -> int: next_=self.get_next_(needle) i=0 j=0 res=False while i<len(haystack): while haystack[i]!=needle[j] and j>0: j=next_[j-1] if haystack[i]==needle[j]: j+=1 if j==len(needle): return True i+=1 return False pass def get_next_(self,s: str): next_=[0]*len(s) i=1 k=0 while i<len(s): while s[i]!=s[k] and k!=0: k=next_[k-1] if s[i]==s[k]: k+=1 next_[i]=k i+=1 return next_