字符串系列2--KMP


原题描述

输入

第一行一个整数N,表示测试数据组数。

接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。

其中N<=20

输出

对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。

样例输入

    5
    HA
    HAHAHA
    WQN
    WQN
    ADA
    ADADADA
    BABABB
    BABABABABABABABABB
    DAD
    ADDAADAADDAAADAAD

样例输出

    3
    1
    3
    1
    0

KMP算法

KMP算法是单模式串的字符匹配算法,如果需要进行多模式串的字符匹配,则需要用到AC自动机算法。

详细的KMP算法可以查看CSDN博客

整个求解的思路可以如下:

  1. 求解Next数组
  2. 进行字符串匹配

求解Next数组的过程:

  1. next[0] = next[1] = 0
  2. 从i=1处开始遍历模式串p,初始j=next[i]
  3. 循环检测 j>0&&p[j]!=p[i],执行j=next[j] ,直到循环退出
  4. 判断 j和i处模式串的值是否相等,若相等,则j++
  5. next[i+1] = j ,继续步骤2遍历

字符串匹配的过程:

  1. 初始化模式串p的遍历j = 0,遍历原字符串s
  2. 循环检测 j>0&&p[j]!=s[i],执行j=next[j] ,直到循环退出
  3. 判断模式串位置j和原串s位置i的值是否相等,若相等,则j++
  4. 如果j==模式串的长度,则说明匹配成功一次,可以进行后续处理,继续步骤2遍历

C++代码

#include <iostream>

using namespace std;

void compute(string p, int* next) {
    int len = p.length();
    next[0] = next[1] = 0;
    int j = 0;
    for (int i = 1; i < len; i++) {
        j = next[i];
        while (j > 0 && p[j] != p[i]) {
            j = next[j];
        }
        if (p[j] == p[i]) {
            j++;
        }
        next[i+1] = j;
    }
}

int match(string s, string p, int* next) {
    int j = 0;
    int counts = 0;
    for (int i = 0; i < s.length(); i++) {
        while (j > 0 && s[i] != p[j]) {
            j = next[j];
        }
        if (s[i] == p[j]) {
            j++;
        }
        if (j == p.length()) {
            counts++;
            j = next[j];
        }
    }
    return counts;
}

int main(void) {
    int nums;
    string s, p;
    int *next;
    cin >> nums;
    while (nums--) {
        cin >> p;
        cin >> s;
        next = new int[p.length() + 1];
        compute(p, next);
        cout << match(s, p, next) << endl;
        delete next;
    }
    return 0;
}

Bug Free Procedure 从记事本直接写代码

2016.12.16 Bingo!!!一次性AC
1. 要注意在match函数里面对模式串的j的判断



本作品由 Marvin.Cao 创作,采用 CC BY-NC-SA 3.0 许可协议 进行许可。

Comments