现在的位置: 首页 > 综合 > 正文

串匹配之KMP

2017年04月27日 ⁄ 综合 ⁄ 共 1154字 ⁄ 字号 评论关闭

首先我给你两个串,在A串(长n)中找B串出现的首个位置(长m),

可以十分轻松的想到O(nm)的算法。。

但我们十分愉快友好的发现,有些匹配是多余的。。

然后我们发现

如果

a[] = abbababab b[] = abbb

a[1] = b[1] a[2] = b[2]

a[3] = b[3] a[4]!=b[4]

此时如果从a[2]开始重新枚举真是一种愚蠢的行为。。

如果a[]中包含的第一个b[]串中包含着a[4](假设起点位于x)

那么,可以得到,a[x...4] = a[1...(4-x+1)];

为何?因为我们已经得到了a[1...3]=b[1...3]

而b[1...3] = a[x...x+2];

所以如果答案是[1,4]中某点x

肯定存在:x <= a[1...4]的最长(后缀==前缀)的后缀的起点

似乎是一个不错的优化

所以我们只要求出每一位的最长的(后缀==前缀)的后缀的起点就行了。。

这个似乎很难求啊。。。我们开一个next[]数组来储存这个;

但我们轻松愉快的发现对于每一位来说:具有最优子结构。。无后效性。。。

PS:接下来的应该可以自己推出。。。

所以我们可以通过next[1...i-1]和a[1....i] 来判断next[i]

如果a[i] == a[  next[i-1]  + 1   ]

那么很容易的得到next[i] = next[i-1]+1;

如果不相同。。判断 a[i] = a[ next[next[i-1]] + 1 ](为什么?画图想想)

以此类推

测试题目:http://www.wikioi.com/problem/1204/

细节问题可以自己想想,这儿给出我的代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define MAX 1000001
char a[MAX],b[MAX];
int next[MAX];
int kmp(char *a,char *b)
{
	int la = strlen(a+1),lb = strlen(b+1),ans = 0;
	for (int i = 2; i <= la; i++)
	{
		int j = next[i-1];
		while(j && a[i]!=a[j+1])
			j = next[j];
		next[i] = j?j+1:(a[i]==a[1]);
	}
	for (int i = 1, j =1; j <= lb;)
	{
		if ( a[i] == b[j] )
		{
			if ( i >= la )
			{
				return j-i+1;
				ans++;
				i = next[i];
			}
			i++;
			j++;
		}
		else
		{
			while(i>1 && a[i]!=b[j])
				i = next[i-1]+1;
			if ( i == 1 )
			{
				i++;
				j++;
			}
		}
	}
	return ans;
}
int main()
{
	scanf("%s%s",a+1,b+1);
	printf("%d\n",kmp(b,a));
	return 0;
}

抱歉!评论已关闭.