1. 题目描述
题目来源:力扣
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
输入:haystack = "hello", needle = "ll" 输出:2
输入:haystack = "aaaaa", needle = "bba" 输出:-1
2. 题解
2.1 KMP算法基础
KMP算法的思路如图所示,
图1. KMP算法匹配过程
字符“b”的前缀aa和后缀aa是一致的,因此将指针位置挪到b进行匹配。如何找到b,通过“前缀表”。
在一段字符串中,前缀和后缀的概念如下,以“aabaaf”字符串为例
- 前缀:包含首字母,但不包含尾字母的连续字符串;
- 后缀:包含尾字母,但不包含首字母的连续字符串。
(1) 前缀
- a
- aa
- aab
- aaba
- aabaa
(2) 后缀
- f
- af
- aaf
- baaf
- abaaf
(3) 子串的最大相等前后缀长度
a:没有前缀和后缀,因此L = 0
aa:前缀,a;后缀,a,L = 1(a)
aab:前缀包含a aa;后缀,ab a,L=0
aaba:前缀,a aa aab;后缀,a ba aba,L=1(a)
aabaa:前缀,a aa aab aaba;后缀,a aa baa abaa,L=2(aa)
aabaaf:前缀,a aa aab aaba aabaa;后缀,f af aaf baaf abaaf ,L=0。
因此字符串“aabaaf”的前缀表为next = [0 1 0 1 2 0]。
2.2 算法流程
为得到前缀表next,设计两个指针,分别指向前缀的结尾位置j,和后缀的结尾位置i。
(1)初始化:
首字母位置的前缀表next[0] = 0
前缀的位置j=0
后缀的位置i=1—len(s)遍历
(2)前缀和后缀不相等:
while s[j] != s[i],j = next[j-1],即前缀退到上一个使得上一个使得前后缀相等的位置,直至挪到0为止。(i前标记的永远是和j前j个长度相同的字符串,因此,j既是前缀的结尾,也是最大前后缀相等的长度)
(3)前后缀相等时:
前缀j和后缀i同时向后移动一个位置。(看到下一个位置时,当前的前后缀末尾是否相同(前面的已经j个相同))
j++
next[i] = j
该过程的理解图示如下,
图2. 前缀表的计算过程
在最开始的问题,寻找一个字符串T是否包含某个子串s,过程如下,
T的索引i从0—len(T)遍历,s的索引j从0开始
(2)判断,如果当前字符不对应,那么退回到前一个子字符串的最大长度的前缀长度位置开始重新匹配,(到最大相等前缀的结尾,重新开始匹配,前面都是匹配的)到j=1时停止,if s[j] != T[i],j = next[j-1],
(3)当前字符串相等的话(即不是退回到0的位置),那么,
j++ (待匹配的索引++)
i++(给定文本的索引++)
若当前已经到了s的末尾,退出。
2.2 代码
该过程的代码如下,
class Solution(object):
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
if haystack == "":
return 0
# 初始化前缀表,第一个的最大相等前后缀长度为0
next = []
next.append(0)
index_pre = 0
for index_for in range(1, len(needle)):
while needle[index_pre] != needle[index_for] and index_pre > 0:
index_pre = next[index_pre-1]
if needle[index_pre] == needle[index_for]:
index_pre += 1
next.append(index_pre)
# 根据前缀表找到在haystack中needle首字母出现的位置
j = 0
for i in range(len(haystack)):
while haystack[i] != needle[j] and j > 0:
# 前一位置的最大前缀长度
j = next[j - 1]
# 若是相等,则haystack和needle同时向下移动
if haystack[i] == needle[j]:
j += 1
if j == len(needle):
return (i - len(needle) + 1)
return -1
if __name__ == '__main__':
sol = Solution()
haystack = "hello"
needle = "ll"
r = sol.strStr(haystack, needle)
print(r)