算法算法与数据结构数据结构和算法分析

字符串匹配KMP算法

2019-02-23  本文已影响0人  学无止境1980

假设我们的字符串母串是T,子串是W,我们想找到子串在母串中出现的位置并统计总的出现次数,可以使用KMP算法。KMP算法需要先求解失配函数next(i),失配函数的意义如下:T_{[0, i-1]}的最长公共前后缀是T_{[0, next(i)-1]}T_{[0, N]}的最长公共前后缀是T_{[0, k]}的含义是k是最大的满足k<NT_{[0, k]}=T_{[N-k, N]}的非负整数)。

求失配函数需要用到全局变量:

const int MAXW = 10000+5;
char W[MAXW]; // 子串
int nex[MAXW]; // 失配函数,即next(i)=nex[i]

求失配函数的代码如下:

nex[0] = -1; // 特殊规定nex[0]的值为-1
int i, j;
for(i=0;W[i];i++){
    // 根据nex[i]的值求nex[i+1]的值
    // nex[i]=0表示不存在最长公共前后缀
    j = nex[i];
    while(j>=0 && W[j]!=W[i]) j = nex[j];
    nex[i+1] = j+1;
}

但实际上失配函数还有可以优化的地方,对于T_{[0, i-1]}的最长公共前后缀T_{[0, next(i)-1]},假如T_i=T_{next(i)},则在T_i处失配时必定也将继续在T_{next(i)}处失配,故可以改进失配函数。改进失配函数next(i)的含义如下:T_{[0, next(i)-1]}T_{[0, i-1]}的最长的满足T_i\ne T_{next(i)}的公共前后缀。

求改进失配函数的代码如下:

nex[0] = -1; // 特殊规定nex[0]的值为-1
int i, j;
for(i=0,j=-1;W[i];i++){
    // 根据nex[i]的值求nex[i+1]的值
    // nex[i]=0表示不存在最长公共前后缀
    // 此时j等于未改进时的失配函数nex[i]
    while(j>=0 && W[j]!=W[i]) j = nex[j];
    nex[i+1] = W[i+1]==W[++j] ? nex[j] : j;
}

求好了失配函数之后,便可以利用失配函数进行字符串匹配了。需要用到的全局变量:

const int MAXT = 1000000+5;
const int MAXW = 10000+5;
char T[MAXT], W[MAXW]; // T是母串,W是子串
int nex[MAXW]; // 之前求出的失配函数

KMP算法利用失配数组进行字符串匹配的具体代码如下:

int i, j, ans = 0; // ans表示W在T中出现的次数
for(i=j=0;T[i];i++){
    // 此时已经匹配到了W[j-1]和T[i-1]
    while(j>=0 && W[j]!=T[i]) j = nex[j];
    if(!W[++j]) ans++;
}

附上一道KMP模板题:POJ3461 Oulipo

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXW = 10000+5;
const int MAXT = 1000000+5;
int nex[MAXW];
char W[MAXW], T[MAXT];
int main(){
    int TC; scanf("%d", &TC);
    while(TC--){
        scanf("%s%s", W, T);
        memset(nex, 0, sizeof(nex));
        nex[0] = -1;
        int i, j, ans = 0;
        for(i=0,j=-1;W[i];i++){
            while(j>=0 && W[j]!=W[i]) j = nex[j];
            nex[i+1] = W[i+1]==W[++j] ? nex[j] : j;
        }
        for(i=j=0;T[i];i++){
            while(j>=0 && W[j]!=T[i]) j = nex[j];
            if(!W[++j]) ans++;
        }
        printf("%d\n", ans);
    }
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读