KMP字符串匹配算法

KMP算法是一种高效的字符串匹配算法,通过预处理部分匹配表(next数组)避免不必要的回溯。算法时间复杂度为O(n+m),其中n和m分别为文本串和子串的长度。文章提供了算法步骤、部分匹配表构建方法以及样例解释。
摘要由CSDN通过智能技术生成

KMP字符串匹配算法

KMP算法(Knuth-Morris-Pratt Algorithm)是一种高效的字符串匹配算法,用于在一个较长的文本串中查找特定的子串。KMP算法的时间复杂度为O(n+m),其中n为文本串的长度,m为子串的长度。相比于朴素的字符串匹配算法,KMP算法更加高效。

原理

KMP算法的核心思想是在匹配过程中,利用已经匹配过的字符信息,避免在文本串中进行不必要的回溯。KMP算法通过预处理子串,得到一个称为“部分匹配表”的数组(也称为“失败函数”或“next数组”),用于记录子串中的前缀和后缀信息。

部分匹配表(next数组)

部分匹配表是一个长度为m的数组,用于存储子串的每个字符对应的最长相同前后缀长度。例如,给定子串ABCDABD,其部分匹配表如下:

字符 A B C D A B D
next 0 0 0 0 1 2 0

构建部分匹配表的方法是遍历子串,利用动态规划的思想计算每个字符对应的最长相同前后缀长度。

KMP算法步骤

  1. 构建子串的部分匹配表。
  2. 在文本串中进行匹配。遍历文本串,从左到右与子串进行匹配。
    • 当文本串和子串的字符相等时,将文本串和子串的指针向右移动。
    • 当文本串和子串的字符不相等时,将子串的指针向左移动至next数组对应的位置,而文本串的指针不回溯。
    • 当子串的指针移动至最后一个字符,说明找到了匹配的子串。记录匹配位置,并将子串的指针重置至初始位置。

例题

【模板】KMP字符串匹配

题目描述

给出两个字符串 s 1 s_1 s1 s 2 s_2 s2,若 s 1 s_1 s1 的区间 [ l , r ] [l, r] [l,r] 子串与 s 2 s_2 s2 完全相同,则称 s 2 s_2 s2 s 1 s_1 s1 中出现了,其出现位置为 l l l
现在请你求出 s 2 s_2 s2 s 1 s_1 s1 中所有出现的位置。

定义一个字符串 s s s 的 border 为 s s s 的一个 s s s 本身的子串 t t t,满足 t t t 既是 s s s 的前缀,又是 s s s 的后缀。
对于 s 2 s_2 s2,你还需要求出对于其每个前缀 s ′ s' s 的最长 border t ′ t' t 的长度。

输入格式

第一行为一个字符串,即为 s 1 s_1 s1
第二行为一个字符串,即为 s 2 s_2 s2

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s 2 s_2 s2 s 1 s_1 s1 中出现的位置。
最后一行输出 ∣ s 2 ∣ |s_2| s2 个整数,第 i i i 个整数表示 s 2 s_2 s2 的长度为 i i i 的前缀的最长 border 长度。

样例 #1

样例输入 #1

ABABABC
ABA

样例输出 #1

1
3
0 0 1

提示

样例 1 解释

对于 s 2 s_2 s2 长度为 3 3 3 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 1 1 1

数据规模与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(30 points): ∣ s 1 ∣ ≤ 15 |s_1| \leq 15 s115 ∣ s 2 ∣ ≤ 5 |s_2| \leq 5 s25
  • Subtask 2(40 points): ∣ s 1 ∣ ≤ 1 0 4 |s_1| \leq 10^4 s1104 ∣ s 2 ∣ ≤ 1 0 2 |s_2| \leq 10^2 s2102
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 1 ≤ ∣ s 1 ∣ , ∣ s 2 ∣ ≤ 1 0 6 1 \leq |s_1|,|s_2| \leq 10^6 1s1,s2106 s 1 , s 2 s_1, s_2 s1,s2 中均只含大写英文字母。

#include <iostream>
#include <string>
#include <string.h>
#include <vector>
using namespace std;
const int N=1e6+10;
char s[N],p[N];
int ne[N];
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin>>s>>p;
    int n=strlen(s);
    int m=strlen(p);
    vector<int> ans;
    //计算ne数组
    ne[0]=-1;
    for(int i=1,j=-1;i<m;i++){
        while(j!=-1&&p[i]!=p[j+1])j=ne[j];
        if(p[i]==p[j+1])j++;
        ne[i]=j;
    }

    //匹配
    for(int i=0,j=-1;i<n;i++){
        while(j!=-1&&s[i]!=p[j+1])j=ne[j];
        if(s[i]==p[j+1])j++;
        if(j==m-1){
            j=ne[j];
           ans.push_back(i-m+1);
        }
    }
    for(auto t:ans){
        cout<<t+1<<endl;
    }
    for(int i=0;i<m;i++)cout<<ne[i]+1<<" ";
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

跑跑跑啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值