常用算法——模式匹配(KMP)

来源:互联网 发布:vmware nat 端口转发 编辑:程序博客网 时间:2024/06/07 16:33

在一个字符串中查找一个子字符串有很多方法,最简单容易想到的算法便穷举,但这样的情况下算法复杂度为O(m * n)。
而KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度。
KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值越大,当然之前可能出现再匹配的机会就越大。

先来个图表来表示一下next数组的求解方式:
这里写图片描述
可是如何去求next数组每个数组元素的值呢?
-1. 逐个查找对称串
比如i = 0时,串对应的字符是a,不存在对称,所以next[0] = 0;
同样的,i = 1, 2, 3时 ag, agc, agct均不存在对称,所以next[1…3] = 0;
而i = 4时,agcta串的前缀a和后缀a相等,长度为1,所以next[4] = 1;
再比如i = 6时,agctagc的前缀agc和后缀agc相等,长度为3,所以next[6] = 3;
而i = 7时,agctagca只有前缀a和后缀a相等,长度为1,所以next[7] = 1;
之后,便可以次类推。
既然已经可以人工的推出这个next数组的值了,那么编程应该也可实现了:
编程思想如下:
1) 当前面字符的前一个字符的next值为0时,只要将当前字符与子串第一个字符进行比较。前面都是0,说明都不对称了,到当前位置时只是多加了一个字符,要对称的话最多是当前的和第一个对称。此时,如果当前字符和第一个相等,那个该位置的next值为1,否则为0;

2) 依此,可以总结一个规律,不仅当前一个字符的next值为0时,如果前面一个字符的next值是1,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明next就是2了;

3) 同2)中所说如果一直相等,就一直累加。

说到这里,上面情况理解起来应该没什么问题,现在的问题是如果遇到下一个不等,该怎么进行处理?
比如i = 14时,该位置的前一个字符的next值为7,但该位置的next值按照最大公共前后缀(agct)的规则来看,长度为4,其next的值为4。
在这里相当于重新寻找i = 14,更小的对称性,那么:
1)如果要存在对称性,那么对称程度肯定比前一个字符 的对称程度小,所以要找个更小的对称;
2)要找更小的对称,必然在对称内部还存在子对称,而且当前字符还必须紧接着在子对称串之后;
所以,agctagc 和 agctagc对称,agc、agc、agc、agc、对称。

算法实现与测试:

#include<iostream>#include <string>#include <map>#include <vector>#include <cmath>#include <bitset>#include <stack>using namespace std;//next数组void create_next(const string &seq,int *next){    int k;//最大前后缀长度//    int len = seq.size();    //字符串的第一个字符的最大前后缀长度为0    next[0] = 0;    for (int i = 1, k = 0; i < len; ++ i)    {        //求出seq[0]···seq[q]的最大的相同的前后缀长度k        while(k > 0 && seq[i] != seq[k])            k = next[k-1];             //如果相等,那么最大相同前后缀长度加1        if (seq[i] == seq[k])        {            k++;        }        next[i] = k;    }}//KMP算法实现int kmp(const string &str,const string &sub,int *next){    //当前已经匹配的串长度//    int q;    int n = str.size();    int m = sub.size();    create_next(sub,next);    int i;    for (i = 0, q = 0; i < n; ++ i)    {        while(q > 0 && sub[q] != str[i])            q = next[q-1];        if (sub[q] == str[i])        {            q++;        }        if (q == m)        {            break;        }    }        return i - m + 1;}int main() {    string str, sub;    cin >> str >> sub;    int *next = new int[sub.size() + 1];    cout << "start position : " << kmp(str, sub, next) << endl;    system("pause");    return 0;}

测试结果:
这里写图片描述

0 0
原创粉丝点击