首页 > 其他分享 >KMP板子

KMP板子

时间:2023-11-24 19:13:03浏览次数:45  
标签:主串 AC 下标 后缀 next KMP 字符串 板子

update on 2023.11.17 NOIP前来复习板子,发现KMP整理的不是很到位,所以更新详细一些。

模板题


抽象的blog

浅显易懂的讲解视频:(dalao讲得太好了\(%%%\))

备用网址


\(kmp\)(字符串匹配)的概念:

  • 主串:被匹配的字符串
  • 模式串:匹配的串
  • 最长前后缀:一个字符串某个前缀后后缀相同,而且长度尽可能长

先讲暴力算法:
遍历主串,如果有一个字符与模式串不一样,适配了,那么主串就回到原来的位置+1,相当于一个 \(O(n \times m)\) 的复杂度.

因为复杂度过大,所以三位大佬(K,M,P)发明出了KMP算法,他的核心思想就是利用已知的数据,利用好,不往回头走,从而做到 \(O(n+m)\) 的复杂度。

设:

  • 主串的下标是 \(i\)
  • 模式串的下标是 \(j\)

他的做法就是不让i往回走,所以就能达到线性的时间复杂度

首先我们要预处理出 \(next\) 数组。

预处理完后。我们需要遍历一遍主串,按照暴力的思想一个个字符串相匹配,如果失配了。

那么我们设 \(k\) 为: 上一个下标(最后一个正确匹配的下标)的 \(next\) 数组。

然后将 $ j $ 加上 \(k\) ,也就是将模式串的下标往右移动k个下标,这样就可以跳过一些字符,但依然不影响答案。如果有一部分重复的例如: \(ABABC\) 中找 \(ABC\),自然,匹配到 \(C\) 的时候,就失配了。所以就找到第二个字符的 \(next\) 数组的值,为 \(2\),所以直接跳过了前面的 \(AB\),就直接配对了 \(ABC\)。

但是为什么这个 \(next\) 数组可以知道?

因为 \(next\) 数组存储的是主串中从头到这个下标的最长前后缀。如果他们有相同的前后缀,那么就可以跳过这些(注意:最长前后缀的长度不能等于计算的字符串,不然跳过整个字符串还有什么意义?)

当然,计算每一个字符串的最长前后缀也不是暴力的,我们可以分两种情况,如果遍历到 \(i\) ,那么已知\([1,i-1]\)的最长前后缀,假设其长度为\(k\),那么这个字符串的 \([1,k]\) 和 \([i-k,i-1]\) 相等,如果 \(i\) 和 \(k+1\) 能相等,那么 \([1,i]\) 的最长前后缀长度就是 \(k+1\) 否则,我们就按照之前的字符串的前缀。(具体看视频

更新部分:

数学课上拿着NOI辞典上写的一些笔记。。

For example:

第一种情况:

每一次比较 \(S_{next_{i-1} + 1}\) 的位置和 \(S_i\) 对比,如果匹配成功,那么显然是 \(next_i = next_{i-1} + 1\)。

第二种情况:匹配失败

假设首先知道 \(next_{12} = 5\)

ACBACDDACBAC 后面加入一个 B。显然 D 不等于 B。所以此时我们就要往前跳。

我们发现:ACBACDDACBAC 中前面的 ACBAC 的后面的 AC 和后面的 ACBAC 的后面的 AC说的可能有点绕,不太好讲,具体还是看图吧。两个字符串相等,他们的后缀也一定相等。(说了跟说了似的

然而,前面的 ACBAC 的 \(next\) 数组是 \(2\)。因此其中的两个 AC 也是一样的。

ACBACDDACBAC 的前缀 AC 和后缀 AC 是一样的,于是下标往右移,发现左边的 AC + B 是与新加入的 B 相匹配。

于是就可以知道 ACBACDDACBACB 的 \(next\) 数组是 \(2+1=3\)。

因此,讲完了,感觉还是图画的清楚点。

实现流程:根据书中的话来说:

(1)设 \(j\) 为 \(next_{i-1}\) ,\(S_{j+1} = s_i\) 那么 \(next_i = j+1\)

(2)若不相等,\(j\) 需要变得更小,以满足 \(S_{[1,j]} = S_{[i-j,j-1]}\) 的最大值。即 \(j = next_j\) (其实上文主要解释了 \(j\) 为什么会等于 \(next_j\)。若此时 \(S_{j+1} = S_i\) 那么 \(next_i = j+1\)

(3)若仍然不相等,重复(2)直到 \(j=0\) 为止。
https://cdn.luogu.com.cn/upload/image_hosting/obc9fny4.png


AC Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+5;
char s1[N],s2[N];
int l1,l2;
int nxt[N];
int main(){
	scanf("%s",s1+1);l1 = strlen(s1+1);
	scanf("%s",s2+1);l2 = strlen(s2+1);
	for(int i = 2,j = 0;i <= l2;++i){
		while(j&&s2[i] != s2[j+1])j=nxt[j];
		if(s2[i]==s2[j+1]) ++j;
		nxt[i]=j;
	}
	for(int i = 1,j = 0;i <= l1;++i){
		while(j && s1[i] != s2[j+1])j=nxt[j];
		if(s1[i]==s2[j+1]) ++j;
		if(j==l2) printf("%d\n",i-l2+1),j=nxt[j]; 
	}
	for(int i = 1;i <= l2;i++) printf("%d ",nxt[i]);
	return 0;
}

有什么错误欢迎指出。

标签:主串,AC,下标,后缀,next,KMP,字符串,板子
From: https://www.cnblogs.com/gsczl71/p/17854552.html

相关文章

  • KMP与自动机
    KMP与AC自动机都是字符串匹配KMP是单模匹配ac自动机是多模匹配KMP原理例子当我们匹配字符串A(长度为n)中是否有B(长度为m,m<n)的时候比如:AABCDABCDEFBABCDE一个朴素的思路是暴力,复杂度当然是O(n*m)KMP就是一个优化的算法KMP的理论基础就是,并不是每次匹......
  • KMP模板
    #include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e3+10,inf=0x3f3f3f3f;intnex[N];//nex[j]的意思是当子串的第j个字符和主串的第i个字符不匹配时,我们应该从子串的nex[j]字符开始重新匹配stringa,b;/*kmp指针回退j=nex[j-1]......
  • 计算几何板子
    #definei128longlonginlinei128ABS(i128x){returnx<0?-x:x;}structfrac{ i128x,y; frac(){} frac(i128xx,i128yy=1ll):x(xx),y(yy){ if(y<0)x=-x,y=-y; } friendfracoperator+(fraca,fracb){returnfrac(a.x*b.y+a.y*b.x,a.y*b.y);} friendf......
  • 扩展 KMP——Z 函数
    本文下标从\(0\)开始。建议:前置知识。扩展KMP(Z函数)我们已经认识了前缀函数了。它是维护一个字符串的所有前缀的最长公共真前后缀的长度——\[\overbrace{s_0\dotss_{\pi(i)-1}}~s_{\pi(i)}\dotss_{i-\pi(i)}~\overbrace{s_{i-\pi(i)+1}\dots\color{red}s_i}~s_{i+1}......
  • kmp算法
    2023-11-14作用:从一个字符串中找到另一个字符串的位置思路:    暴力匹配-》主串的指针一直往前走,不后退-》匹配串的指针回退的位置变小-》根据前缀表 求前缀表(匹配串的所有前缀的最长公共前后缀长度表):/求前缀表int[]next=newint[needle.length()];......
  • 写板子的时候发现的易错点
    KMPvoidget_nt(){intj=0;for(inti=2;i<=tl;++i){while(j&&t[i]!=t[j+1])j=nt[j];if(t[j+1]==t[i])j+=1;nt[i]=j;}}voidKMP(){intj=0;F(i,1,sl){while(j&&s[i]!=t[j+1])j=nt[j]......
  • RKMPP 硬编码之mpi_enc_test .c解析
    一.简介mpi_enc_test是rockchip官方编码demo本篇文章进行mpi_enc_test的代码解析,编码流程解析二.环境介绍硬件环境:ArmSoM-W3RK3588开发板软件版本:OS:ArmSoM-W3Debian11三.mpp编解码流程解析<center>图3.1RKMPP编码器接口为用户提供了输入图像数据,输出码......
  • 大非质数取模算组合数板子
    constintN=1e5+10,M=13;intn,mod,l,r;llans,p[M],br[M],phi;inlinellksm(lla,llb){ lld=1; while(b){ if(b&1)d=d*a%mod; a=a*a%mod; b>>=1; } returnd;}namespacebig_mod{ structnum{ llx,r[M]; }fac[N],I,B; inlinenumoperat......
  • 写了个高精度加法板子
    #include<bits/stdc++.h>using namespace std;const int N=1e4+9;int a1[1000],b1[1000],ans[1000];void add(int a[],int b[],int na,int nb){int t=0;if(na<nb)return add(b,a,nb,na);for(int i=0;i<na;i++){t+=a[i];if(i<nb)t+=b[i];ans[i]=t%10;t/=10;}if(......
  • 【板子申请】Ai-M61-32S开发环境搭建-wuboy19
    【板子申请】Ai-M61-32S开发环境搭建-wuboy19window10vscode环境安装vscode官网下载windows版本图1官网界面图图2安装成功图博主百度网盘下载百度网盘链接提取码:9jydgit安装git官网下载链接图3git安装过程图博主网盘下载git百度网盘链接提取码:n3z......