KMP算法与前缀函数

更多文章可以在本人的个人小站:https://kaiserwilheim.github.io 查看。
转载请注明出处。

简介

KMP算法,全称为 Knuth-Morris-Pratt 算法,是由 Knuth, Morris 和 Pratt 这三个人创造的算法,可以在 O ( n + m ) O(n+m) O(n+m) 的时间内使用 O ( n ) O(n) O(n) 的空间完成如下的任务:

给定一个字符串 S S S 和一个模式串 T T T,求出 S S S T T T 中所有出现的位置。

其中 ∣ S ∣ = n |S| = n S=n ∣ T ∣ = m |T| = m T=m

KMP算法主要依赖的是 “Next函数” 这个东西。

Next函数

Next函数,有时候也被称作 “前缀函数”,是KMP算法的核心部分。
我们以一个数组 π \pi π 来表示它。

其旨在求得任意一个前缀的border长度。

什么是border?

border指的是一个字符串内,真前缀和真后缀相等的那一部分。
这样的真前缀和真后缀可能有很多种,我们需要找的是最长的那一组。

真前缀和真后缀说的是前缀和后缀中除去字符串本身之后剩下的部分。

如何求得border?

朴素算法

我们显然可以暴力扫,最终的复杂度是 O ( n 3 ) O(n^3) O(n3) 的。

懒得写了,直接搬了OI-Wiki的代码。

// C++ Version
vector<int> prefix_function(string s)
{
   
	int n = ( int )s.length();
	vector<int> pi(n);
	for(int i = 1; i < n; i++)
		for(int j = i; j >= 0; j--)
			if(s.substr(0, j) == s.substr(i - j + 1, j))
			{
   
				pi[i] = j;
				break;
			}
	return pi;
}
# Python Version
def prefix_function(s):
	n = len(s)
	pi = [0] * n
	for i in range(1, n):
		for j in range(i, -1, -1):
			if s[0 : j] == s[i - j + 1 : i + 1]:
				pi[i] = j
				break
	return pi

优化

我们会发现,相邻的两个函数值最多会增加1。

也就是说,当我们移动到下一个位置时,Next函数的值要么增加一,要么维持不变,要么减少。

此时改进的算法如下:

// C++ Version
vector<int> prefix_function(string s)
{
   
	int n = ( int )s.length();
	vector<int> pi(n);
	for(int i = 1; i < n; i++)
		for(int j = pi[i - 1] + 1; j >= 0; j--)  // improved: j=i => j=pi[i-1]+1
			if(s.substr(0, j) == s.substr(i - j + 1, j))
			{
   
				pi[i] = j;
				break;
			}
	return pi;
}
# Python Version
def prefix_function(s):
	n = len(s)
	pi = [0] * n
	for
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值