KMP总结及拓展

之前集训的时候曾经自学过KMP,好像就把模板打了就走了,没有深入理解KMP,今天借着老师讲解,重新加强巩固了一番,仅以此篇纪念。

声明:此篇博客不是教学,仅作为之后复习的资料。

KMP解决的是字符串匹配问题,当询问一个较短串在一个较长串中出现的次数及位置时,就可以用KMP算法了。

模板

给定两个由小写字母构成的字符串 L 和 S 。
请你从左到右,找出子串 L 在母串 S 中每次出现的开始位置(匹配位置)。

#include<bits/stdc++.h> 
using namespace std; 
 
char s[1000005],t[1000005]; 
int Next[1000005]; 
int lens,lent; 
 
void getNext() //计算Next数组 
{ 
  int j=1,k=0; 
  Next[1]=0; 
  while(j<lent) 
  { 
    if(k==0||t[j]==t[k]) 
      Next[++j]=++k; 
    else k=Next[k]; 
  } 
} 
 
int main() 
{
  scanf("%s",s+1); lens=strlen(s+1);  //从下标1开始存储字符串s  
  scanf("%s",t+1); lent=strlen(t+1);  //从下标1开始存储字符串t  
  getNext();                           //计算Next数组  
    int i=1,j=1,f=0;    
  while(i<=lens) 
  { 
    if(j==0||s[i]==t[j]) i++,j++; 
    else j=Next[j];                  //t串指针j跳转到Next[j]继续匹配  
    if(j>lent)                       //找到匹配位置 
    { 
      cout<<i-lent<<endl; 
      i--;                        //更新i,继续匹配 
      j=Next[j-1];                //更新j,继续匹配 
      f=1;                        //匹配的标记 
    } 
  } 
  if(f==0) cout<<"NO"<<endl;          //找不到匹配位置 
  return 0; 
}
常数优化

其实没啥用,还会阻碍KMP的其他用法。但对于某些常熟大的人来说,还是必要的… …

void getNext()
{ 
  int j=1,k=0; 
  Next[1]=0; 
  while(j<lent) 
  { 
    if(k==0||t[j]==t[k]) 
    { 
      j++; 
      k++; 
      if(t[j]==t[k]) Next[j]=Next[k]; //优化  
      else Next[j]=k; 
    } 
    else k=Next[k]; 
  } 
}

最长公共前后缀问题

因为 n x t nxt nxt[ ]数组维护的是 s [ 1 ] s[1] s[1]~ s [ l e n − 1 ] s[len-1] s[len1] s [ l e n ] s[len] s[len])的最长公共前后缀,所以可以解决此类问题。

一道板子:

HDU2596 Simpsons’Hidden Talents

这道题唯一需要动脑子的就是要想到将两个串拼接在一起。其余用KMP的 g e t n x t ( ) getnxt() getnxt()函数计算就可以了。

#include<bits/stdc++.h>
using namespace std;

char s[100005];
int len,len1,nxt[100005];

void getnxt()
{
	for(int i=2,j=0; i<=len; i++)
	{
		while(j&&s[j+1]!=s[i]) j=nxt[j];
		if(s[j+1]==s[i]) ++j; nxt[i]=j;
	}
}

int main()
{
	while(scanf("%s",s+1)!=EOF)
	{
		len1=strlen(s+1);
		scanf("%s",s+len1+1);
		len=strlen(s+1);
		memset(nxt,0,sizeof(nxt));
		getnxt();
		if(!nxt[len]){puts("0");continue;}
		for(int i=1;i<=nxt[len];i++) cout<<s[i];
		cout<<" ";
		printf("%d\n",nxt[len]);
	}
	return 0;
}

求最长循环节问题

又一道板子:

Poj2406 power strings

#include<bits/stdc++.h>
using namespace std;

int len,nxt[1000005];
char s[1000005];

void getnxt()
{
	for(int i=2,j=0;i<=len;i++)
	{
		while(j&&s[i]!=s[j+1]) j=nxt[j];
		if(s[j+1]=s[i]) j++;nxt[i]=j;
	}
}

int main()
{
	while(scanf("%s",s+1),s[1]!='.')
	{
		len=strlen(s+1);
		memset(nxt,0,sizeof(nxt));
		getnxt();
		int ans=1;
		if(len%(len-nxt[len])==0) ans=len/(len-nxt[len]);
		printf("%d\n",ans);
	}
	return 0;
}

详解见博客:https://blog.csdn.net/deritt/article/details/50765974

撒花完结!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值