kmp算法主要是可以高效地在一堆字符串里找到你想要的子串
算法的原理
核心思想:当某个字符匹配失败时,利用之前已经匹配成功的信息,知道部分前缀与后缀是相等的,因此可以利用这些信息跳过一些不必要的比较。
KMP算法中引入了一个关键的数组next,它用于记录模式串中前后字符重复出现的个数,以便在匹配失败时能够知道应该跳过多少字符。next数组的计算基于模式串本身,与主串无关。
比如
有一个主串
ccaaaacdde
和一个子串
acaaaca
加红a的位置是i,c是j+1
此时已经出现不匹配的了但子串匹配并不会从头开始因为此时以j为下标的相等前缀后缀值是1,所以是从下标为1再开始匹配,这就节省了一部分时间
next数组的意思
-
核心思想:当某个字符匹配失败时,利用之前已经匹配成功的信息,知道部分前缀与后缀是相等的,因此可以利用这些信息跳过一些不必要的比较。
-
next数组:KMP算法中引入了一个关键的数组next,它用于记录模式串中前后字符重复出现的个数,以便在匹配失败时能够知道应该跳过多少字符。next数组的计算基于模式串本身,与主串无关。
-
下面是kmp算法的例题 洛谷p3375
题目描述
给出两个字符串 𝑠1s1 和 𝑠2s2,若 𝑠1s1 的区间 [𝑙,𝑟][l,r] 子串与 𝑠2s2 完全相同,则称 𝑠2s2 在 𝑠1s1 中出现了,其出现位置为 𝑙l。
现在请你求出 𝑠2s2 在 𝑠1s1 中所有出现的位置。定义一个字符串 𝑠s 的 border 为 𝑠s 的一个非 𝑠s 本身的子串 𝑡t,满足 𝑡t 既是 𝑠s 的前缀,又是 𝑠s 的后缀。
对于 𝑠2s2,你还需要求出对于其每个前缀 𝑠′s′ 的最长 border 𝑡′t′ 的长度。输入格式
第一行为一个字符串,即为 𝑠1s1。
第二行为一个字符串,即为 𝑠2s2。输出格式
首先输出若干行,每行一个整数,按从小到大的顺序输出 𝑠2s2 在 𝑠1s1 中出现的位置。
最后一行输出 ∣𝑠2∣∣s2∣ 个整数,第 𝑖i 个整数表示 𝑠2s2 的长度为 𝑖i 的前缀的最长 border 长度。输入输出样例
输入 #1复制
ABABABC ABA
输出 #1复制
1 3 0 0 1
- 以下是AC代码
#include<bits/stdc++.h> using namespace std; const int N = 1e6+10; int ne[N]; char s1[N],s2[N]; int main(){ cin.tie(0);cout.tie(0); cin>>s1+1>>s2+1;//从1开始好处理 int x=strlen(s1+1); int y=strlen(s2+1); //求next数组 for(int i=2,j=0;i<=y;i++){ while(j&&s2[i]!=s2[j+1])j=ne[j];// if(s2[i]==s2[j+1])j++; ne[i]=j; } for(int i=1,j=0;i<=x;i++){ while(j&&s1[i]!=s2[j+1])j=ne[j]; if(s1[i]==s2[j+1])j++; if(j==y){ cout<<i-y+1<<endl; j=ne[j]; } } for(int i=1;i<=y;i++){ cout<<ne[i]<<' '; } return 0; }