kmp + exkmp

kmp :主要就是用于暴力回退的优化 一般的暴力回退总是回退到前一个 ,要枚举很多次 如果找到规律那么就会发现可以找到上一次最大匹配的位置然后将继续匹配知道匹配不下去然后去更新
代码
kmp是前缀 到某一个为停止

#include <bits/stdc++.h>
using namespace std;
int n,m;
int nxt[M+1],f[N+1];
char s[N+2],p[M+2];     //p数组是去配对的数组  s是被配对的!
void kmp()
{
	n=strlen(s+1);
	m=strlen(p+1);
	int j=0;
	nxt[1]=0;
	for(int i=2;i<=m;i++)//直接去拿目标数组去弄是一样的!!本质上找的就是一个前缀等于后缀
		//那么就是一样的呢1
	{
		while(j>0&&p[j+1]!=p[i])   
		j=nxt[j];
		if(p[j+1]==p[i])
		j++;//找到一样的在当前的位置++就是到了下一个
		//如ababac
		//ababac   //第一个本身就是0退无可退 那么第二个又不相等
		//001230  c这里:目前是j是3但是4所代表的不和c一样故3=nxt[3]=1 退到一去 
		nxt[i]=j;//否则就是0
	}
	j=0;
	for(int i=1;i<=n;i++)
	{
		while((j==m)||(j>0&&p[j+1]!=s[i]))//当前已经匹配到最后了
			j=nxt[j];//那么说明不能再往后匹配了
		if(p[j+1]==s[i])
			j++;
		f[i]=j;//能	匹配到第几个!!
	}
}
int main(){}

关键就在于nxt数组的理解:本质上就是p,对p求nxt,跟你被匹配的数组无关 主要就是看如果下一位配不上了我能跳到哪一位知道下一位可以匹配得上 如果实在匹配不上那么就从0开始!!
最长公共前后缀本质就是一个nxt数组,只要明白nxt究竟是个啥就可以啦!!就是由nxt去匹配
p和p+1 去匹配得到的nxt。

接下来就是exkmp
exkmp是从某个位置开始的匹配 ,本质就是维护了一个区间在这个区间的东西和前面的完全相同
而前面的情况之前已经在最开始的

void exkmp()  //最长公共   以第i位开始向后匹配(匹配是从开头开始!
{
	int l=1,r=0;
	z[1]=0; //定义的 从2到n枚举!!   
	//分为多种情况  L记录的最大长度时的i值(若此时的从新的i开始的区间更大 才会被更新 因为记录区间开头这并不重要主要就是区间的结尾!!
	// R 当匹配的区间右端点大于R时才会被更新否则不会被更新! 
	for(int i=2;i<=n;i++)
	{
		if(i>r)//目前的数不在区间内
		{
			z[i]=0;
		}else {
			int k=i-l+1;  //对应  1  -- r-l+1 区间内的数
			z[i]=min(z[k],r-i+1);// 左边是在1 -  r-l+1区间中i对应的位置的z[k]的值若z[k]的值比这个区间的长度都大那么就
			//从第i个位置开始和前面的相等那么就像映射只能保证到r这个位置是一样的超过这个大小就不能保证d
			//故取最小值
			//因为只保证了 从k开始后和i开始后是一样的 那么超过 r-i+1后就不能再保证了
		}
	while(i+z[i]<=n&&s[z[i]+1]==s[z[i]+i])  //之后向后面暴力去找!     z[i]是长度 第一个就枚举第一个位置和当前i的位置是否相等
		++z[i];
	if(i+z[i]-1>r)//只要i>r时有一个配对上会更新!!
		l=i,r=i+z[i]-1;
	}
}


https://www.luogu.com.cn/problem/P4391
这一题是kmp的典型题目

posted @ 2023-04-20 19:47  _Lance  阅读(15)  评论(0编辑  收藏  举报