KMP 以及 border 性质的一些运用

一个 kmp 学了 n 遍终于学懂的屑菜 bot。

下文默认文本串为 s,模式串为 t

前缀函数

定义

πi 表示前缀为 i 的子串中的最长公共前后缀(border)长度。

求取

暴力

O(n3) 去暴力枚举。

高效算法

第一个重要的观察是相邻的前缀函数值至多增加 1

那么我们就是要往前查找一个尽可能大π 满足其往后一位仍然能匹配,或直到 border 长度为 0

具体地可以依照这个视频来看,讲得非常详细。

模板

不难发现实现起来非常之短。

n = s.size(), m = t.size();
	
	s = t + '?' + s;
	
	for(int i = 1, j = nxt[i - 1] ; i < (int)s.size() ; ++ i) {
		while(j && s[i] != s[j]) j = nxt[j - 1];
		
		if(s[i] == s[j]) {
			nxt[i] = ++ j;
			
			if(nxt[i] == m) cout << i - m * 2 + 1 << '\n', j = nxt[j - 1];
		} 
	}

KMP

不难发现,将文本串接在模式串后,中间隔一个特殊字符,若出现 πi=|t| 的情况则说明匹配成功了。

例题

CF126B

如图,pos 为当前枚举到的位置,红色部分和蓝色部分为 [1,pos] 的 border,那么不难发现红 = 蓝 = 绿。

正序、倒序各求一遍 border 即可。

for(int i = 2 ; i < n ; ++ i)
	if(nxt[i][0] == nxt[n - i + nxt[i][0]][1])
		if(nxt[i][0] > ans) {
			ans = nxt[i][0];

			pos = i;
		}

if(! ans) cout << "Just a legend";
else
	for(int i = 0 ; i < nxt[pos][0] ; ++ i)
		cout << s[i];
posted @   end_switch  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示