KMP模板

193 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

暴力算法

image.png

  1. 假设S[N]是要匹配的串,比较长的串
  2. P[M]模板串,比较短的串
  3. KMP习惯下标从1开始
  4. 做法
    1. 第一层循环,i是当前的起点,假设从s[i]开始匹配p能否匹配成功
    2. flag:用来判断是否匹配成功
    3. 第二层循环,j是p的起点

如何优化

  1. next[i]:表示以i为后缀,跟从1开始的前缀相等,而且后缀的长度最长(也就是匹配的长度最长)
    1. 例子:next[i] = jimage.png
      1. 含义:表示以i为后缀,跟从1开始的前缀相等,而且后缀的长度最长为j
      2. 也就是p[1, j] = p[i - j + 1, i]这两段长度相等
  2. 如何应用1.呢?
    1. image.png

题目

www.acwing.com/problem/con… image.png

代码

image.png

#include <iostream>

using namespace std;

const int N = 1e5 + 10, M = 1e6 + 10;

int n, m;
char p[N], s[M]; // p是短串 s是长串
int ne[N]; // next数组

int main()
{
    cin >> n >> p + 1 >> m >> s + 1; // 下标从1开始
    
    // 求next数组 i是从2开始的,因为next[1] = 0(全局数组默认值是0了),如果第一个失败的话,就回到起点了
    for (int i = 2, j = 0; i <= n; i++)
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j++; // 匹配成功了
        ne[i] = j; // 最后记录一下
    }
     
    
    // kmp匹配过程
    // 试图跟si匹配的是p[j + 1],所以j往前错一位
    for (int i = 1, j = 0; i <= m; i++)
    {
        // 如果j没有退回起点,而且s跟p出现第一个不能匹配的点了
        // 那么就将p的j退回到ne[j](跟前面一段的最大匹配)
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j++; // 匹配的话就继续下一位咯
        if (j == n) // 匹配成功
        {
             // 输出匹配的起始位置
            printf("%d ", i - n); // 题目的下标是从0开始的,所以就不用i - n + 1了
            j = ne[j]; // 匹配成功,还要往后退一步
        }
    }
    return 0;
}

时间复杂度

O(2m)