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的求法,只要自己和自己匹配一次即可