【算法入门·字符串】对KMP算法的简单理解

有关KMP算法

K M P KMP KMP算法通俗的说,就是一个字符串 A A A,一个字符串 B B B A A A的长度小于 B B B,可以求 A A A是否是 B B B的子串;若 A A A B B B的子串,甚至还可以求出 A A A B B B中在不同位置里的出现次数。

A A A的长度为 N N N B B B的长度为 M M M,则双重循环暴力匹配的算法的时间复杂度是 O ( N M ) O(NM) O(NM),这显然对于 K M P KMP KMP O ( N + M ) O(N+M) O(N+M)的复杂度有所差异,而 K M P KMP KMP可以在快速的时间内求解这个问题。
(这里会强调一些我之前听挂的内容,所以语言很通俗,不要介意啦)

有关KMP前缀的next数组解法

我们规定 n e x t [ i ] next[i] next[i]表示在 A A A串中长度为 [ 1... i ] [1...i] [1...i]的子串中,靠前靠后最大相同子串的长度。

例如,字符串 A B C D ABCD ABCD中,靠前的表示: A , A B , A B C A,AB,ABC AABABC,表示一定要挨着第一个字母 A A A

同样,靠后的就是 D , C D , B C D 。 D,CD,BCD。 DCDBCD

有例如,在字符串 A C D A B ACDAB ACDAB中, A B AB AB既满足靠前的,也满足靠后的;我们把这样的字符串的最大长度称为 n e x t next next数组的值。

然后,我们在求解 n e x t [ i ] next[i] next[i]的时候,就可以用递推的思想,即如何根据已知 n e x t [ 1 − ( i − 1 ) ] next[1-(i-1)] next[1(i1)]来求。

最暴力的方法就是双重循环进行自我匹配,这显然不可取;我们举一个例子来理解一下吧。

再来个例子, A B C A B A 。 ABCABA。 ABCABA

s t e p 1 : step1: step1匹配 A B C ABC ABC,匹配不到; n e x t [ 1 − 3 ] = 0. next[1-3]=0. next[13]=0.

s t e p 2 : step2: step2匹配第四个 A A A,此时可以和第一个 A A A进行匹配,则 n e x t [ 4 ] = 1. next[4]=1. next[4]=1.

s t e p 3 : step3: step3匹配第五个 B B B,此时可以和第二个 B B B进行匹配,则 n e x t [ 5 ] = 2. next[5]=2. next[5]=2.

关键的step4: 匹配第六个 D D D时,发现第三个 C C C不匹配;此时另一个指针指向的时 3 3 3(主指针指向的时6),此时要重新再前面找到一个子串 A [ 1... j ] 且 j &lt; 2 和 A [ k . . . 6 ] 且 k &gt; 4 A[1...j]且j&lt;2和A[k...6]且k&gt;4 A[1...j]j<2A[k...6]k>4进行匹配;此时我们会发现 A [ 1... j ] = A [ k . . . 6 ] A[1...j]=A[k...6] A[1...j]=A[k...6];此时由于 A [ 1..2 ] A[1..2] A[1..2] A [ 4..5 ] A[4..5] A[4..5]是匹配的,所以 A [ k . . . 6 ] = A [ p . . . 2 ] , A[k...6]=A[p...2], A[k...6]=A[p...2],此时 A [ 1... j ] = A [ p . . . 2 ] A[1...j]=A[p...2] A[1...j]=A[p...2],那么其实就是上面的 n e x t next next数组的含义,前面的和后面的最大长度的字符串。

如果你还是不理解,我们画个图吧。

在这里插入图片描述

然后就上代码了:

for (int i=2,j=0;i<=n;++i)
		{
			while (j>0 && a[i]!=a[j+1]) j=next[j];
			if (a[i]==a[j+1]) j++;
			next[i]=j;
		}

关于两个不同的字符进行匹配

好吧思路其实和上面一样;唯一有一个不通电就是,当指针j=n时表示已经匹配完了,此时我们需要进行j=next[j]操作并统计答案。

现在给出一个给定一个字符串 A 和一个字符串 B,求 B 在 A 中的出现次数 这道题的模板。

#include<bits/stdc++.h>
using namespace std;
int ans=0;
char a[2000000];
char b[2000000];
int next[2000000];
int main(void)
{
	freopen("kmp.in","r",stdin);
	freopen("kmp.out","w",stdout);
	cin>>a+1>>b+1;
	int len1=strlen(a+1);
	int len2=strlen(b+1);
	for (int i=2,j=0;i<=len2;++i)
	{
		while (j>0 && b[i]!=b[j+1]) j=next[j];
		if (b[i]==b[j+1]) j++;
		next[i]=j;
	}
	for (int i=1,j=0;i<=len1;++i)
	{
		while (j>0 && a[i]!=b[j+1]) j=next[j];
		if (a[i]==b[j+1]) j++;
		if (j==len2) ans++,j=next[j];
	}
	cout<<ans<<endl;
	return 0;
} 

KMP例题[Usaco2015 Feb]Censoring

这道题就是一个KMP模版的变式,因为中间要不断删除,两边会产生连接,这正好符合栈这一个数据结构。

因为按照KMP的算法来,算一个进栈一个;如果匹配了,则弹出所有匹配的字符串,继续在栈上面进行匹配;这样,既能够进行正常的KMP匹配算法,还可以进行删除和拼接的操作。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int top=0;
int s[2000000];
int f[2000000];
char a[2000000];
char b[2000000];
int next[2000000];
int main(void)
{
	freopen("censor.in","r",stdin);
	freopen("censor.out","w",stdout);
	cin>>a+1>>b+1;
	int len1=strlen(a+1);
	int len2=strlen(b+1);
	for (int i=2,j=0;i<=len2;++i)
	{
		while (j>0 && b[i]!=b[j+1]) j=next[j];
		if (b[i]==b[j+1]) j++;
		next[i]=j;
	}
	for (int i=1,j=0;i<=len1;++i)
	{
		while (j>0 && a[i]!=b[j+1]) j=next[j];
		if (a[i]==b[j+1]) j++;
		f[i]=j;
		s[++top]=i;
		if (f[i]==len2)
		{
			top-=len2;
			j=f[s[top]];
		} 
	}
	for (int i=1;i<=top;++i) cout<<a[s[i]];
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值