首页 > 其他分享 >从KMP到exKMP

从KMP到exKMP

时间:2024-08-13 20:19:16浏览次数:14  
标签:exKMP nxt 匹配 int ++ 后缀 KMP strlen

KMP(Knuth-Morris-Pratt)

用途:

用于一个文本串S内查找一个模式串P 的出现位置,以及求一个字符串的最小循环元长度和最大循环次数。

思路:

\(kmp\)是对原始的在文本串S内查找一个模式串P的出现位置的一种优化。

原始做法

将\(s\)的每一位都与\(p\)的第一位开始匹配。

(匹配到\(s\)的第\(i\)位和\(p\)的第\(j\)位。\(i\),\(j\)从0开始)

if(s[i]==p[j])i++,j++;//匹配成功,同时加1
else i=i-j+1,j=0;//j变成0,匹配的起始点向右移一位

\(kmp\)做法

匹配不成功,\(i\)不变,\(j=nxt[j]\)

\(nxt[j]\)表示\(j\)的最长前后缀,eg. \(abacaba\)中\(aba\)是最长前后缀,\(a\)也是它的前后缀但不是最长的;

这样匹配,合理的运用了前面匹配的信息,只有前后缀才有可能成为答案,理由很显然,这样可以避免一些无用的枚举。

直接求\(nxt\)t的复杂度是\(n^2\),所以考虑优化。

i的前后缀一定是\(i-1\)的前后缀\(+1\),因为将\(i\)掉,剩下的一定满足前后缀。

所以只要不停跳\(i-1\)的前后缀,可以快速求\(nxt\).

void gnxt()
{
	int j=0;
	int len=strlen(p+1);
	for(int i=2;i<=len;i++)
		while(j&&p[i]!=p[j+1]) j=nxt[j];//找一个前后缀满足下一位相同
		if(p[i]==p[j+1])j++;//符合条件j匹配下一位
		nxt[i]=j;//记录
	}
	return ;
}

模式串与文本串匹配与求\(nxt\) 的过程相似,

	int s1=strlen(s+1);
	int s2=strlen(p+1);
	int i=0,j=0;
	for(int i=1;i<=s1;i++)
	{
		while(j&&s[i]!=p[j+1]) j=nxt[j];
		if(s[i]==p[j+1])j++;
		if(j==s2)printf("%d\n",i-j+1),j=nxt[j];
	}
	return ;

EXKMP

用途:

能在 \(O(|a|+|b|)\) 的时间内处理出文本串 \(a\)的所有后缀和模式串 \(b\) 的最长公共前缀。

流程(图片来自洛谷):

求\(nxt[i]\)(\(nxt[i]\)表示模式串\(b\)与模式串\(b\)以\(b[i]\) 开头的后缀的最长公共前缀的长度)如下图

图片 0

假设求\(nxt[x]\)时所有\(nxt[j](0\le j<x)\) 都已经求出来了。

定义\(p\) 是\(max(i+nxt[i]-1)\),\(k\)是这个最大的\(i\).可知蓝色段相同

图片 1

加上当前点\(x\) ,可知绿色段相同

图片 2

因为绿色段相同,\(nxt[x-k]\) 对计算 \(nxt[x]\) 有帮助。

如果\(x-k+nxt[x-k]-1<nxt[k]-1\) 那么 \(nxt[x]=nxt[x-k]\) (显然灰色部分相同且\(l-1\)到 \(x-k\)的蓝色部分与剩下的绿色不同,不然\(nxt[x-k]\) 应该包括它们)

图片 3

如果比它大,显然 \(nxt[x]>=(nxt[k]-1-(x-k))\) 整个绿色都是公共前缀,\(p\) 的后面不一定相同所以,\(p\) 和\(l-1\)一个一个暴力比较计算\(nxt[x]\) 。

图片 4

正确性显然,时间复杂度参考\(manacher\) 算法。

void qnxt(char *c)
{
	int len = strlen(c);
	int p = 0, k = 1, l; //我们会在后面先逐位比较出 nxt[1] 的值,这里先设 k 为 1
	//如果 k = 0,p 就会锁定在 |c| 不会被更改,无法达成算法优化的效果啦
	nxt[0] = len; //以 c[0] 开始的后缀就是 c 本身,最长公共前缀自然为 |c|
	while(p + 1 < len && c[p] == c[p + 1]) p++;
	nxt[1] = p; //先逐位比较出 nxt[1] 的值
	for(int i = 2; i < len; i++)
	{
		p = k + nxt[k] - 1; //定义
		l = nxt[i - k]; //定义
		if(i + l <= p) nxt[i] = l; //如果灰方框小于初始的绿方框,直接确定 nxt[i] 的值
		else
		{
			int j = max(0, p - i + 1);
			while(i + j < len && c[i + j] == c[j]) j++; //否则进行逐位比较
			nxt[i] = j;
			k = i; //此时的 x + nxt[x] - 1 一定刷新了最大值,于是我们要重新赋值 k
		}
	}
}
  • 求\(ext[i]\)

我们用 \(ext[i]\) 表示“模式串 \(b\)” 与 “文本串 \(a\) 以 \(a[i]\) 开头的后缀” 的 “最长公共前缀” 的长度。

与求\(nxt[i]\) 的方法相同,\(nxt[i]\)是自己与自己匹配而已

void exkmp(char *a, char *b)
{
	int la = strlen(a), lb = strlen(b);
	int p = 0, k = 0, l;//ext[0]!=|c|,所以预处理0,从一开始递推
	while(p < la && p < lb && a[p] == b[p]) p++; //先算出初值用于递推
	ext[0] = p;
	for(int i = 1; i < la; i++) //下面都是一样的逻辑啦
	{
		p = k + ext[k] - 1;
		l = nxt[i - k];
		if(i + l <= p) ext[i] = l;
		else
		{
			int j = max(0, p - i + 1);
			while(i + j < la && j < lb && a[i + j] == b[j]) j++;
			ext[i] = j;
			k = i;
		}
	}
}

标签:exKMP,nxt,匹配,int,++,后缀,KMP,strlen
From: https://www.cnblogs.com/storms11/p/18024012

相关文章

  • 字符串查找 - 模拟实现strstr 、BF算法 、 KMP算法
    文章目录前言一、模拟实现库函数strstr二、BF算法三、KMP算法总结前言路漫漫其修远兮,吾将上下而求索。一、模拟实现库函数strstrTips:此处采用利用指针+字符串末尾'\0'的判断,当然你可以利用数组的下标;库函数strstr的原型:char*strstr(constchar*str1,......
  • KMP算法的两种实现形式
    以leetcode28.实现strStr()为例:给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从0开始)。如果 needle 不是 haystack 的一部分,则返回  -1 。示例1:输入:haystack="sadbutsad",needle="sad"......
  • KMP&exKMP
    (之前学的一些东西都没打笔记,给忘的差不多了。从这个开始要记得写笔记了。)注意事项:所有的字符串的下标从1开始。KMP对于一个字符串s,定义它的前缀数组a,其中a[i]表示子串s[1...i]前缀与后缀相同的最大长度(不包括串自身)。对于朴素的算法,自然是n^2的暴力。考虑利用前面位置的值......
  • kmp算法(c++)
    kmp算法的简单介绍从主串中快速找到与要找的串的相同位置如果使用暴力算法去求解这个问题,时间复杂度为O(i*j)=>很大kmp算法则是对这类问题的优化因整理过于麻烦,,详细的介绍可以参照这篇博客,,花时间看完就明白了,写的很棒!!!kmp算法详细介绍下面是自己学习的一些注意的地......
  • kmp算法模板
    模板//pi代表前缀函数 //pi[i]:s[0~i]的最长匹配真前后缀长度 vecotr<int>pi(str.size()); //求前缀函数 for(inti=1;i<str.size();i++){ intlen=pi[i-1];//前一个值的pilen是我们想要找到的一个长度值 while(len!=0&&str[i]!=str[len]){//不匹配时,......
  • KMP 与 Z 函数拓展
    【失配树:KMP拓展】先KMP一遍。然后对\(0\simn\)建立一棵树:\(nxt[i]\)作为\(i\)的父结点。则最长公共border就是这棵树上的LCA对应的长度。border:若\(a\)既是\(s\)的前缀又是\(s\)的后缀,则\(a\)是\(s\)的border。周期:若\(s\)以\(p\)为周期,则\(s[......
  • KMP算法
     ......
  • KMP算法
    写在前面喜报:听了四遍都没学懂的KMP算法,终于在gyy大佬的耐心讲解下搞懂了,大佬orz!!!正文kmp算法本质上就是对模式串(要匹配的子串两个串中短的那个)中很多重复的前缀和后缀索引起来,使得在一个地方失配了也不要紧,不用重新来的算法(看不懂不要紧)下面我们就来详细介绍一下kmp的几个......
  • KMP学习笔记
    KMP一种字符串单模匹配算法。原理当模式串\(s\)与文本串\(t\)进行匹配时,容易想到的一种朴素做法就是将模式串的第一位与文本串的每一位进行试配。但是这样效率过低,容易被数据卡成\(O(n^2)\)。KMP单模匹配算法引入了一个失配数组border。定义一个字符串的border为一......
  • KMP 算法学习笔记
    问题引入给出两个字符串\(s1\)和\(s2\),求出\(s2\)在\(s1\)中所有出现的位置(出现指\(s1\)中存在子串与\(s2\)完全相同)。朴素暴力不详细介绍,容易发现时间复杂度不优秀。KMP算法思想在朴素暴力中我们可以发现有很多匹配是不需要再次从头开始重新匹配的,举个例子:ABA......