KMP字符串模式匹配算法

简介

        KMP算法是一种改进的字符串匹配算法,由D.E.Knuth、J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

KMP基本概念:

KMP步骤:

1.前缀表。

2.匹配字符串。

前缀表:

对next[j] ,是p[1,j]串中每一位前缀和后缀相同的最大长度。

它存储的是每一个下标对应的“前缀表”,是KMP算法的核心。

变量:

s[ ]是模式串,即比较长的字符串。
p[ ]是模板串,即比较短的字符串。

前后缀:

“前缀”:指除了最后一个字符以外,字符串的全部有序组合。
“后缀”:指除了第一个字符以外,字符串的全部有序组合。

核心思想:

在每次失配时,不是把模版串往后移一位,而是把模版串往后移动至下一次可以和前面部分匹配的位置,这样就可以跳过大多数的失配步骤。而每次模版串移动的步数就是通过查找next数组确定的。

例题

AcWing  831

给定一个模式串 S,以及一个模板串 P

所有字符串中只包含大小写英文字母以及阿拉伯数字

模板串 P 在模式串 S 中多次作为子串出现。

求出模板串 P 在模式串 S 中所有出现的位置的起始下标

输入格式

第一行输入整数 N,表示字符串 P 的长度。

第二行输入字符串 P。

第三行输入整数 M,表示字符串 S 的长度。

第四行输入字符串 S。

输出格式

共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。

数据范围

1≤N≤1e5
1≤M≤1e6

输入样例:

3
aba
5
ababa

输出样例:

0 2

代码:

#include 
using namespace std;

const int N = 1e5 + 10, M = 1e6 + 10; // N为模版串长度,M为模式串长度
char p[N], s[M];                      // p为模版串,s为模式串
int ne[N];                            //前缀表,即模版串最长公共前缀后缀长度

int main()
{
    int n, m;                                   //实际长度
    scanf("%d %s %d %s", &n, p + 1, &m, s + 1); //数组下标从1开始
    for (int i = 2, j = 0; i <= n; i++)         //求前缀表,即next数组。i为后缀,j为前缀
    {
        while (j && p[i] != p[j + 1]) //如果后缀不等于前缀,返回前一位的最长公共前缀后缀长度
        {
            j = ne[j];
        }
        if (p[i] == p[j + 1]) //如果匹配,则移至下一位
        {
            j++;
        }
        ne[i] = j; //写入当前位的最长公共前缀后缀长度
    }
    for (int i = 1, j = 0; i <= m; i++) //匹配字符串操作
    {
        while (j && s[i] != p[j + 1])
        {
            j = ne[j];
        }
        if (s[i] == p[j + 1]) //如当前位的模式串和模版串相等,移至下一位
        {
            if (++j == n) //判断是否完全匹配
            {
                printf("%d ", i - n); //输出匹配的首字母下标
                j = ne[j];            //读取当前位的最长公共前缀后缀长度,以便再次匹配
            }
        }
    }
    puts(""); //换行
    return 0;
}

你可能感兴趣的:(c++,算法,经验分享,数据结构)