字符串——kmp


一、前言

kmp算法是用于从文本串text的字串中,寻找含有的模板串pattern的数量/位置的算法。
例如,在文本串abcabcccabc中,模板串abc的数量有3个,其起始位置是0,3,8。

二、思路

暴力是两个for循环O(n*m)搞定,显然不够优雅,而kmp则是O(n+m)
kmp说实话,有点绕,很多人不知道next数组的意义,但其实可以这样理解:
寻找模板串中最长的相同后缀与前缀,并通过next数组存储
如何理解?举个栗子
对于模板串abcab而言,我们从头开始构造next数组。

字串 前缀 后缀 最长公共前后缀 next数组
"a" [] [] next[0] = 0
"ab" [a] [b] next[1] = 0
"abc" [a,ab] [c,bc] next[2] = 0
"abca" [a,ab,abc] [a,ca,bca] a next[3] = 1
"abcab" [a,ab,abc,abca] [b,ab,cab,bcab] ab next[4] = 2
"abcabc" [a,ab,abc,abca,abcab] [c,bc,abc,cabc,bcabc] abc next[5] = 3

对于文本串abcabb来说,当匹配到5,即abcabb时,模板串匹配到abcabc,此时该位置字符不同

当使用暴力算法时,文本串必须跳回1位置,即b,而模板串必须跳回初始位置,重新匹配

当使用kmp算法时,模板串next[当前位置-1 = 4] = 2,即跳到2位置,此时模板串为abc,而文本串为abcabb

发现了吗?
模板串前缀abc与文本串后缀abb拥有相同前缀ab,于是文本串就不需要回到原点了,可以继续对比,即此时文本串为abcab,而模板串为ab,然后对比接下来的字符。

三、代码

int nextt[maxn];
void get_nextt(char pattern[]){//为pattern字符串创建nextt数组
    nextt[0] = 0;
    int max_length = 0;
    for(int i = 1;pattern[i];i++){
        // 这里是想要获取前缀的最长相同前后缀
        while(max_length > 0 && pattern[max_length] != pattern[i])
            max_length = nextt[max_length-1];
        if(pattern[i] == pattern[max_length])
            max_length++;
        nextt[i] = max_length;
    }
}
queue<int> search(char text[],char pattern[]){//从test字符串中,寻找含有多少个pattern字符串,并将其开头位置存入队列中
    queue<int> q;
    int pattern_length = strlen(pattern);
    get_nextt(pattern);
    int count = 0;
    for(int i = 0;text[i];i++){
        while(count > 0 && pattern[count] != text[i])
            count = nextt[count-1];
        if(pattern[count] == text[i])
            count++;
        if(count == pattern_length){
            q.push(i-pattern_length+1 );
            count = nextt[count-1];
        }
    }
    return q;
}
原文地址:https://www.cnblogs.com/MMMMMMMW/p/11607518.html