KMP算法

简介

s1:ABCAABCADFABCAs1:\tt{ABCAABCADFABCA}

s2:ABCAs2:\tt{ABCA}

s1s1 的开始下标(从 11 开始):1,5,111,5,11

可以在 Θ(n)\Theta(n) 的时间复杂度内将 s2s2s1s1 匹配。

预处理

首先有一个定义:真前(后)缀是不包含原串的前(后)缀字串。

我们定义 fif_i 为以第 ii 个字符为结尾的字符串的真后缀与真前缀匹配的最大长度。
以下我们模拟对一个字符串求 ff

abcabcd\tt{abcabcd}

  • f1=0f_1=0。没有真前(后)缀。
  • f2=0f_2=0
  • f3=0f_3=0
  • f4=1f_4=1s[1]s[1]s[4]s[4] 匹配。
  • f5=2f_5=2s[1,2]s[1,2]s[4,5]s[4,5] 匹配。
  • f6=3f_6=3s[1,3]s[1,3]s[4,6]s[4,6] 匹配。
  • f7=0f_7=0

特别地,f0=0f_0=0


接下来介绍求法。

我们设 ii 为处理到了 s[i]s[i]jj 为前缀最大匹配到长度为 jj 的子串,当前要匹配 s[i],s[j]s[i],s[j]。(因为字符串从 0 开始存储,所以 j1+1(j)j-1+1(j) 也是接下来要匹配的字符串)

开始时,i=j=0i=j=0i0n1i \gets 0\sim n-1

s[i]s[j]s[i]\neq s[j] 时(失配),此时的 jj 就是不合法的,考虑移动 jj。如果暴力重新匹配,时间花费大。

此时要想起 fif_i 为第 ii 个字符为结尾匹配的最大长度,此时的 fjf_j 就表示以第 jj 个字符结尾匹配的的最大长度。新的 jj 肯定也要和 sis_i 匹配。所以使 jfjj\gets f_j,找到能匹配的最大长度。

s[i]=s[j]s[i]=s[j] 时,jj+1j\gets j+1


匹配

只需要按照同样的步骤,当匹配的长度等于匹配串时,说明匹配成功。

Code

#include<bits/stdc++.h>
using namespace std;
int f[1000005];
void init(string &s) {
	int len=s.size(),j=0;
	f[0]=f[1]=f[2]=0;
	for(int i=1;i<len;i++) {
		while(j&&s[i]!=s[j]) j=f[j];
		if(s[i]==s[j]) j++;
		f[i+1]=j;//第 i 个字符 
	}
}
void kmp(string &s1,string &s2) {
	int n=s1.size(),m=s2.size();
	for(int i=0,j=0;i<n;i++) {
		while(j&&s1[i]!=s2[j]) j=f[j];
		if(s2[j]==s1[i]) j++;
		if(j==m) {
			cout<<i-m+2<<"\n";
			j=f[j];
		}
	}
}
string s1,s2;
int main() {
	cin>>s1>>s2;
	init(s2);
	kmp(s1,s2);
	int l=s2.size();
	for(int i=1;i<=l;i++) {
		cout<<f[i]<<" ";
	}
	return 0;
}

本文作者:cjrqwq

本文链接:https://www.cnblogs.com/yfzqwq/p/18492795

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   cjrqwq  阅读(2)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
展开
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.