字符串匹配 KMP算法

来源:互联网 发布:单片机芯片破解 编辑:程序博客网 时间:2024/06/16 16:19

首先先来看一个问题,给出两个字符串,S和T
问T在S中出现了多少次,不允许重复,但允许相互有相同部分。
首先想到的肯定是暴力,可以想到,从S的第一个字符开始,看是否和T相等,然后向后移,统计有多少个相等,那么时间就是O(n×m)
当然,这个速度是非常慢的,如果两个串的长度很长,或者有多组数据,自然就超时了。
显然,在匹配的时候,如果遇到不相等的情况,移到下一位的时候,有很大的几率不会相等,那么可以用一个next数组,记录失配的时候,向右移几位就可以让前面的依然匹配。这就是KMP算法的核心。
这时候就可以这样做:
首先从第一个字符开始,如果相等那么向后匹配,如果失配那么就跳到next位置。如果长度与T的长度相等,那么计数器就加1
那么,有了next,就很容易计算答案了,以下为代码

int kmp() {    get_next();//计算next    int ans = 0;    k=0;    for (int i = 0; i < lenS; ++i)    {        while (k > 0 && S[i] != T[k]) {            k = _next[k - 1];//如果失配,那么移动k        }         if (S[i] == T[k]) {            ++k; //如果相等,判断下一位        }        if (k == lenT) { //找到了            k = _next[k - 1];             ++ans; //统计找到的串的个数        }    }    return ans;}

注意一个很重要的东西:next 在一个库中是一个函数,最好不要调用,所以写成了_next
那么,如何求next呢?
其实,求next也相当于是一个在T之中与T匹配的样子,先来看看代码。

void get__next() {    _next[0] = 0;     k = 0;    for (int i = 1; i <= lenT; ++i) {          while (k > 0 && T[i] != T[k]) {            k = _next[k - 1]; // 如果找不到,向后移        }        if (T[i] == T[k]) {            ++k; // 如果找到了 向后移        }        _next[i]=k; // 记录    }}

看起来和匹配很像对不对……
事实上,的确和匹配很像。
可以发现,next记录的就是T的一个前缀的结尾和T的开头最多能够匹配的位数。匹配时,k记录的就是S的前缀和T的开头能够匹配的位数。
同时,求到k的时候,k前面的next都是求好的。
所以类似这样的方法,去用T去匹配T,直接套用前面的匹配过程。
最后再分析一下时间复杂度。
既然它这么神奇,
那么时间复杂度肯定比O(n×m)要小,所以它的时间复杂度应该是线性的,于是来证明一下.
在匹配的时候,在S串中,k每一次while至少前进1,所以最多前进n次,而且最多会后退n次,因此匹配部分的时间复杂度应该为O(n)
那么,求next类似,需要O(m)的时间,所以最后总共需要O(m+n)的时间
这样,KMP就顺利地学完了……

0 0
原创粉丝点击