KMP字符串匹配

洛谷板题P3375
AC Code

点击查看代码
// Problem: P3375 【模板】KMP字符串匹配
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3375
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <iomanip>
#define MAXN 1000050
using namespace std;
string s,t;
int lb[MAXN];
int main(){
	cin>>s>>t;
	int l1=s.length();
	int l2=t.length();
	
	lb[1]=0;
	int j=0;
	for(int i=2;i<=l2;i++){
		while(j&&t[i-1]!=t[j])	j=lb[j];
		if(t[i-1]==t[j])	j++;
		lb[i]=j;
	}
	j=0;
	for(int i=1;i<=l1;i++){
		while(j&&s[i-1]!=t[j])	j=lb[j];
		if(s[i-1]==t[j])	j++;
		if(j==l2){
			cout<<i-j+1<<endl;
			j=lb[j];
		}
	}
	for(int i=1;i<=l2;i++){
		cout<<lb[i]<<' ';
	}
	return 0;
}`

TIPS!!!!
下文中:第x位指的是下标,第x个指的是第几个字符
本文默认字符串下标从0开始!!!!!!(困扰我好久)

首先,为什么要有KMP算法?
一般来说,如果做字符串匹配,比如在ABABACAB里匹配ABACAB吧
首先
ABABACAB
ABACAB
程序会扫描,发现第四位不一样,对吧
于是,暴力做法就会
ABABACAB
ABACAB
开始扫描
你会发现:哎你这不对啊,你这有问题啊,这AB都重复,为什么不能直接跳两次到:
ABABACAB
ABACAB
这一步到位多是一件美事啊???

所以这就祭出了KMP的精髓,一次跳跃好几位进行匹配,预处理这个跳跃能跳多少(细节在下面),这不就美滋滋了吗??
铺垫知识:border
一个字符串S的border是啥呢,前缀和后缀相同
比如对于ABABAB
它的border就是AB,因为开头有AB,结尾也有AB
又对于AACCAA,它的border有两个,A和AA,但AAC不是,因为末尾是CAA,对称是不可以的,只是平移
AA比A长,而且是AACCAA的所有border里面最长的,我们称AA是AACCAA的L Border,简称lb(这是LB不是1B)

那么这个东西和KMP有肾么关联??
一个字符串的border不就是它可以跳跃的距离吗(大体上)
还是
ABABACAB
ABACAB
(下面要查找的字符串成为t串,上面被查找是s串)
发现在第四个字符匹配不上了对吧(注意是第四个不是第四位!)
看一下前三个构成的子串ABA,容易发现lb是A,长度为1
所以,既然匹配到第三个字符后面就匹配不上,可以知道下面:

t已经被匹配的长度为3,t的第三个字符是这个子串的最后一个字符

Key Step:
现在引入变量j,代表t中匹配成功到了第几个字符(注意),对于上面的情况,j=3
同时有变量i,代表在s中匹配到了第几个字符(注意没有像j一样带上“成功”)
现在明确知道j+1就匹配不上了,又舍不得直接只往后移动一位,怎么办??
还记得lb吗?ABA的lb是A,长度为1,也就是说,在ABA的两端各有一个lb,而且左侧的lb最后一位位置是第(lb的长度)个,同时又因为j在已匹配子串ABA的末尾,也就是说——
可以直接把j更新成t的从第一个到第j个字符子串的lb长度!
对于这个例子,把=3的j更新成 这时已匹配子串ABA 的lb 的长度 1

j相对于s的位置没有改变,虽然说是改变j,但其实形象化上是把t向右拉,相对的,相当于j相对于t减小

ABABACAB
ABACAB
第四个字符失配,j=3->查询已匹配子串(1~3)的lb长度->lb长度为1->j=1
而j相对于s位置不变
于是
ABABACAB
j
本来这里j=3,现在j=1,j又不能移动,也就是……
ABABACAB
ABACAB
完美!
不过有时仅仅查询一次lb是不够的,有可能依然i和j+1配不上,这时候再查询j的lb长度的lb长度(对应代码中while,不过要处理j=0时弹出)

对于lb的求法,只要自己和自己匹配一次即可

posted @ 2022-02-08 21:32  Cap1taL  阅读(27)  评论(0)    收藏  举报