KMP
kmp算法只要用在字符串匹配上,当字符串不匹配时,可以知道一部分之前已经匹配的文本内容,避免从头再去匹配
如何记录已经匹配的文本内容,就是kmp算法的重点
前缀表
前缀:不包含最后一个字符的以第一个字符开头的连续子串
后缀:不包含第一个字符的以最后一个字符结尾的连续子串
最长相等前后缀:相同前后缀长度
为什么用前缀表
字符串匹配失败的位置是后缀子串的后一位,那么找到与其相同的前缀的后一位重新匹配就可以了
如何计算前缀表 就是计算next数组
方法一:前缀表统一减一,然后位置统一右移一位,比较s[i]和s[j+1],如果不相等,则j = next[j]
方法二:前缀表统一不减一,比较s[i]和s[j],如果不相等,则j = next[j-1]
一、找出字符串中第一个匹配项的下标
/**
* next数组减1
* @param {string} haystack
* @param {string} needle
* @return {number}
*/
var strStr = function (haystack, needle) {
if(!needle.length) {
return 0
}
let size = haystack.length
let next = getNext(needle)
let j = -1
for(let i = 0; i < size; i++) {
while( j >= 0 && haystack[i] !== needle[j+1]) {
j = next[j]
}
if(needle[j+1] === haystack[i]) {
j++
}
if(j === needle.length -1 ) {
return (i - needle.length + 1)
}
}
return -1
};
function getNext(needle) {
let size = needle.length
let next = new Array(size).fill(-1)
let left = -1
let right = 1
while (right < size) {
if (left >= 0 && needle[left+1] !== needle[right]) {
left = next[left]
}
if (needle[left+1] === needle[right]) {
left++
}
next[right] = left
right++
}
return next
}
/**
* next数组不减1
* @param {string} haystack
* @param {string} needle
* @return {number}
*/
var strStr = function (haystack, needle) {
if(!needle.length) {
return 0
}
let size = haystack.length
let next = getNext(needle)
let j = 0
for(let i = 0; i < size; i++) {
while( j > 0 && haystack[i] !== needle[j]) {
j = next[j - 1]
}
if(needle[j] === haystack[i]) {
j++
}
if(j === needle.length) {
return (i - needle.length + 1)
}
}
return -1
};
function getNext(needle) {
let size = needle.length
let next = new Array(size).fill(0)
let left = 0
let right = 1
while (right < size) {
if (left > 0 && needle[left] !== needle[right]) {
left = next[left - 1]
}
if (needle[left] === needle[right]) {
left++
}
next[right] = left
right++
}
return next
}
二、重复的子字符串
问题要点
思路,最小重复子串其实就是 字符串去除最长相等前后缀,可以数学证明,那关键就在于通过kmp找到字符串的前缀表,判断字符串的长度可以最小重复子串整除
/**
* @param {string} s
* @return {boolean}
*/
var repeatedSubstringPattern = function(s) {
let size = s.length
function getNext(s) {
let left = 0
let right = 1
let next = new Array(size).fill(0)
while(right < size) {
while(left > 0 && s[left] !== s[right]) {
left = next[left - 1]
}
if(s[left] === s[right]) {
left++
}
next[right] = left
right++
}
return next
}
if(!size) {
return false
}
let next = getNext(s)
if(next[size - 1] !== 0 && size % (size - next[size - 1]) === 0) {
return true
}
return false
};