字符串-kmp
KMP
next
next數組就是一個前綴表(prefix table)。
前綴表有什么作用呢?
前綴表是用來回退的,它記錄了模式串與主串(文本串)不匹配的時候,模式串應該從哪里開始重新匹配。記錄了包含當下字符的最長公共前后綴的長度
匹配過程
要在文本串:aabaabaafa 中查找是否出現過一個模式串:aabaaf。
可以看出,文本串中第六個字符b 和 模式串的第六個字符f,不匹配了。如果暴力匹配,會發現不匹配,此時就要從頭匹配了。
但如果使用前綴表,就不會從頭匹配,而是從上次已經匹配的內容開始匹配,找到了模式串中第三個字符b繼續開始匹配。
那么什么是前綴表:記錄下標i之前(包括i)的字符串中,有多大長度的相同前綴后綴。
文章中字符串的
前綴是指不包含最后一個字符的所有以第一個字符開頭的連續子串。
后綴是指不包含第一個字符的所有以最后一個字符結尾的連續子串。
前綴表要求的就是相同前后綴的長度。
而最長公共前后綴里面的“公共”,更像是說前綴和后綴公共的長度。這其實并不是前綴表所需要的
所以字符串a的最長相等前后綴為0。 字符串aa的最長相等前后綴為1。 字符串aaa的最長相等前后綴為2。 等等…。
next數組的代碼實現
j是前綴的末尾(所以j也是公共最長前后綴的長度 因為從下標0開始的 next[i]也是包含i的公共最長前后綴的長度),i是后綴的末尾
- 初始化 j=0 next[0]=0(0位置回退到0) i從1開始 for(i=1;i<len(s);i++)
- 前后綴不同 s[i]!=s[j] 發生沖突 j回退到j前一位指向的值 因為是個不斷回退的過程 因為可能不止一次沖突 即while(j>0&&s[i]!=s[j] ) j=next[j-1]
(ps 為什么需要退回沖突的前一位 因為沖突前的位置是匹配的 所以退回到前一位所指向的值 即是最長公共前綴的后一位)
如下圖在f處沖突,所以退回到f的前一位a所指向的下標是2 說明最長前綴是2即aa 所以從下標2即b開始匹配
- 前后綴相同 s[i]==s[j] j++ i++
- 更新next數組 next[i]=j
28. 實現 strStr()
實現 strStr() 函數。
給你兩個字符串 haystack 和 needle ,請你在 haystack 字符串中找出 needle 字符串出現的第一個位置(下標從 0 開始)。如果不存在,則返回 -1 。
說明:
當 needle 是空字符串時,我們應當返回什么值呢?這是一個在面試中很好的問題。
對于本題而言,當 needle 是空字符串時我們應當返回 0 。這與 C 語言的 strstr() 以及 Java 的 indexOf() 定義相符。
示例 1:
輸入:haystack = “hello”, needle = “ll” 輸出:2
示例 2:
輸入:haystack = “aaaaa”, needle = “bba” 輸出:-1
示例 3:
輸入:haystack = “”, needle = “” 輸出:0
class Solution:def strStr(self, haystack: str, needle: str) -> int:def getnext(s):next=[]#初始化j=0next.append(0)for i in range(1,len(s)):#前后綴不同 回退while j>0 and s[i]!=s[j]:j=next[j-1]# 前后綴相同 j自增if s[i]==s[j]:j+=1# 更新nextnext.append(j)return nextif needle=='' :return 0 next=getnext(needle)j=0for i in range(len(haystack)):# 字符不匹配 沖突 回退while j>0 and haystack[i]!=needle[j]:j=next[j-1]# 匹配if haystack[i]==needle[j]:j+=1# 說明匹配完成if j==len(needle):return(i-len(needle)+1)return -1459. 重復的子字符串
給定一個非空的字符串 s ,檢查是否可以通過由它的一個子串重復多次構成。
示例 1:
輸入: s = “abab” 輸出: true 解釋: 可由子串 “ab” 重復兩次構成。
示例 2:
輸入: s = “aba” 輸出: false
示例 3:
輸入: s = “abcabcabcabc” 輸出: true 解釋: 可由子串 “abc” 重復四次構成。 (或子串 “abcabc”
重復兩次構成。)
說明 (數組長度-最長相等前后綴的長度) 正好可以被 數組的長度整除,說明有該字符串有重復的子字符串。
數組長度減去最長相同前后綴的長度相當于是第一個周期的長度,也就是一個周期的長度,如果這個周期可以被整除,就說明整個數組就是這個周期的循環。
使用了前綴表統一減一的實現方式
前提:
next[len - 1] !=-1 說明是有重復子串的
next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此時字符串asdfasdfasdf的最長相同前后綴的長度。
(len - (next[len - 1] + 1)) 也就是: 12(字符串的長度) - 8(最長公共前后綴的長度) = 4, 4正好可以被 12(字符串的長度) 整除,所以說明有重復的子字符串(asdf)。
class Solution:def repeatedSubstringPattern(self, s: str) -> bool:def getnext(s):next=[]#初始化j=0next.append(0)for i in range(1,len(s)):#前后綴不同 回退while j>0 and s[i]!=s[j]:j=next[j-1]# 前后綴相同 j自增if s[i]==s[j]:j+=1# 更新nextnext.append(j)return nextnext=getnext(s)rest=len(s)-next[len(s)-1]if len(s)%rest==0 and next[len(s)-1]!=0:return Truereturn False總結
- 上一篇: 哈希表-map(对于python来说是字
- 下一篇: 指定gpu训练