KMP算法用于寻找一个长的字符串是否有字串可以匹配短的字符串
一般来说,暴力的解法是两个循环,复杂度是O(len1*len2)
先定义一些名词:
s[]模式串 p[]已经匹配串 前缀/后缀:相同的字串 部分匹配值:前缀/后缀长度最大值
next[] 部分匹配值表 它用于存储每一个下标对应的部分匹配值。
KMP算法的思路:每一次匹配失败,不是简单的后移一位,而是后移已经匹配的串(我认为这个是重点)到下一个可以部分匹配的位置,这样可以减少没必要的比较
Next数组
next数组的含义:对next[ j ] ,是p[ 1, j ]串中前缀和后缀相同的最大长度(部分匹配值)
即满足:p[ 1, next[ j ] ] = p[ j - next[ j ] + 1, j ]
p | a | b | c | a | b |
下表 | 1 | 2 | 3 | 4 | 5 |
next[] | 0 | 0 | 0 | 1 | 2 |
加入p已经有abcab和s串匹配了,则下一次移动是,应该移动到1的位置做匹配尝试
算法实现
KMP主要分两步:求next数组、匹配字符串。
匹配:
当匹配过程到上图所示时,
s[ a , b ] = p[ 1, j ] && s[ i ] != p[ j + 1 ] 此时要移动p串(不是移动1格,而是直接移动到下次能匹配的位置)
其中1串为[ 1, next[ j ] ],3串为[ j - next[ j ] + 1 , j ]。由匹配可知 1串等于3串,3串等于2串。所以直接移动p串使1到3的位置即可。这个操作可由j = next[ j ]直接完成。 如此往复下去,当 j == m时匹配成功。
code:
for(int i = 1, j = 0; i <= n; i++)
{
while(j && s[i] != p[j+1]) j = ne[j];
//如果j有对应p串的元素, 且s[i] != p[j+1], 则失配, 移动p串
//用while是由于移动后可能仍然失配,所以要继续移动直到匹配或整个p串移到后面(j = 0)
if(s[i] == p[j+1]) j++;
//当前元素匹配,j移向p串下一位
if(j == m)
{
//匹配成功,进行相关操作
j = next[j]; //继续匹配下一个子串
}
}
求next数组:
next数组的求法是通过模板串自己与自己进行匹配操作得出来的
code:
for(int i = 2, j = 0; i <= m; i++)
{
while(j && p[i] != p[j+1]) j = next[j];
if(p[i] == p[j+1]) j++;
next[i] = j;
}