kmp总结

本文详细介绍了KMP算法的使用,通过多个实例展示了如何利用KMP算法解决字符串匹配问题,包括计数子串、求子串出现次数、求母串中重复子串的次数和首地址、求循环节问题等。文章提供了多种模板题,帮助读者深入理解并应用KMP算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                 搞了几天的kmp,这个NEXT数组真心博大精深啊,到现在还有些next数组还不会用。

          hdu 3336  Count the string 这道题就是对next数组的运用,可以得出一个公式,b[i]=b[next[i]]+1;把b数组全加起来就是所有前缀的和。

#include<iostream>
#include<cstring>
using namespace std;
int next[200001],n,b[200001],m;
char a[200001];
void getnext(char *p,int *next)
{
  int i,j;
  next[0]=-1;
  i=0;
  j=-1;
  while(i<m)
  {
      if(j==-1||p[i]==p[j])
      {
       i++;
       j++;
        next[i]=j;
      }
     else
      j=next[j];
  }
}
int main()
{
  int i,sum;
  scanf("%d",&n);
    while(n--)
    {
        sum=0;
        scanf("%d",&m);
      scanf("%s",a);
      getnext(a,next);
      b[0]=0;
      for(i=1;i<=m;i++)
      {
        b[i]=(b[next[i]]+1)%10007;
        sum=(sum+b[i])%10007;
      }
      printf("%d\n",sum);
    }
  return 0;
}


hdu 1686 Oulipo      求子串在母串中出现的次数,也差不多是kmp模板题。。。

#include<stdio.h>
#include<string.h>
char a[10001],b[1000001];
int next[10001],x,y;
void getnext()
{
  int i,j;
  next[0]=-1;
  i=0;
  j=-1;
  while(i<x)
  {
      if(j==-1||a[i]==a[j])
      {
        i++;
        j++;
        next[i]=j;
      }
      else
          j=next[j];
  }
}
int kmp()
{
  int i,j,sum=0;
  i=0;j=0;
  
  while(i<y)
  {
    if(j==-1||b[i]==a[j])
    {
       i++;
       j++;
    }
    else
        j=next[j];
    if(j==x)
    {
      sum++;
      j=next[j];
    }
  }
  return sum;
}
int main()
{
  int n;
 scanf("%d",&n);
  while(n--)
  {
  scanf("%s %s",a,b);
  x=strlen(a);
  y=strlen(b);
getnext();
  printf("%d\n",kmp());
   }
  return 0;
}


hdu 1711  Number Sequence kmp模板题不解释。。。

#include<iostream>
using namespace std;
int n,m;
int a[1000001],b[10001];
int next[10001];
void getnext(int *p,int *next)
{
  int i,j;
  next[1]=0;
  i=1;
  j=0;
   while(i<m)
   {
     if(j==0||p[i]==p[j])
     {
      i++;
      j++;
      next[i]=j;
     }
     else
         j=next[j];
   }
}
int kmp(int *s,int *p)
{
  int i,j;
  i=1;
  j=1;
  getnext(p,next);
  while(i<=n)
  {
    if(j==0||s[i]==p[j])
    {
      i++;
      j++;
    }
    else
        j=next[j];
    if(j==m+1)
        return i-m;
  }
  return -1;
}
int main()
{
  int i,t;
  scanf("%d",&t);
  while(t--)
  {
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=m;i++)
        scanf("%d",&b[i]);
    printf("%d\n",kmp(a,b));
  }
  return 0;
}


hdu 1358 Period  对next数组的运用,求母串中重复子串的次数和首地址。。这个重复子串必须是连续的,像aabaababbaab,最后一个aab就不算啦。。还有一个求周期问题,len-next[i]为此字符串的最小循环节(i为字符串的结尾),另外如果len%(len-next[i])==0,此字符串的最小周期就为len/(len-next[i]);

 

#include<iostream>
#include<cstring>
using namespace std;
char a[1000001];
int next[1000001],n;
void getnext(char *p,int *next)
{
  int i,j;
  i=0;
  j=-1;
  next[0]=-1;
  while(i<n)
  {
    if(j==-1||p[i]==p[j])
    {
     i++;
     j++;
     next[i]=j;
    }
    else
        j=next[j];
  }
}
int main()
{
  int i,j,x,y;
  j=1;
  while(~scanf("%d",&n)&&n)
  {
      getchar();
    scanf("%s",a);
    getnext(a,next);
    printf("Test case #%d\n",j);
    for(i=1;i<=n;i++)
    {
      x=i%(i-next[i]);
      y=i/(i-next[i]);
      if(x==0&&y>1)
        printf("%d %d\n",i,y);
    }
    j++;
    printf("\n");
  }
}


hdu 3746 Cyclic Nacklace 求循环节问题,也是对next数组的运用。。。。

给你一个字符串,要求将字符串的全部字符最少循环2次需要添加的字符数。

例子:

abcabc 已经循环2次,添加数为0

abcac 没有循环2次,添加字符abcac。数目为5.

abcabcab 已经循环过2次,但第三次不完整,需要添加数为1

#include<iostream>
#include<cstring>
using namespace std;
int n,next[100001],len;
char a[100001];
void getnext(char *p,int *next)
{
  int i,j;
  next[0]=-1;
  i=0;
  j=-1;
  while(i<len)
  {
    if(j==-1||p[i]==p[j])
    {
      i++;
       j++;
      next[i]=j;
    }
    else
        j=next[j];
  }
}
int main()
{
    int i,j,x;
   scanf("%d",&n);
   while(n--)
   {
     scanf("%s",a);
     len=strlen(a);
     getnext(a,next);
     j=len-next[len];
     x=len%j;
     //for(i=0;i<=len;i++)
    //     printf("%d ",next[i]);
    if(j!=len&&x==0)
      printf("0\n");
    else
        printf("%d\n",j-next[len]%j);
   }
   return 0;
}


hdu 2594 Simpsons' Hidden Talents  求第一个字符串的前缀与第二个字符串的后缀的最长公共子串,也是一道模板题。。。我这里用的事优化版的next数组,注意最后一个next数组的值。。。

 

#include<iostream>
#include<cstring>
using namespace std;
char a[50001],b[50001];
int next[50001],x,y;
void getnext()
{
  int i,j;
  next[0]=-1;
  i=0;
  j=-1;
  while(i<x)
  {
    if(j==-1||a[i]==a[j])
    {
      i++;
      j++;
     if(a[i]!=a[j])
          next[i]=j;
      else
          next[i]=next[j];
    }
    else
        j=next[j];
  }
}
int kmp()
{
  int i,j;
  j=0;
  i=0;
  getnext();
  while(i<y)
  {
    if(j==-1||b[i]==a[j])
    {
      i++;
      j++;
    }
    else
        j =next[j];
  }
  return j;
}
int main()
{
    int i,n,j,k;
  while(cin>>a)
  {
      cin>>b;
      x=strlen(a);
      y=strlen(b);
      n=kmp();
     if(n==0)
     printf("0");
     else
     {
          for(i=0;i<n;i++)
             printf("%c",a[i]);
          printf(" %d",n);
     }
     printf("\n");
  }
  return 0;
}


下面是poj上的,真心觉得hdu上的题目难度系数大的到poj上就成了水题啦。。。我基本做的都是水题,学这个算法,这些题来做还是很好理解的,容易上手。。现在从hdu转到poj上做啦。。。。

poj 2406 Power Strings 求周期。。

#include<iostream>
#include<cstring>
using namespace std;
char a[1000001];
int next[1000001],len;
void getnext()
{
  int i,j;
  next[0]=-1;
  i=0;j=-1;
  while(i<len)
  {
    if(j==-1||a[i]==a[j])
	{
	  i++;
	  j++;
	  next[i]=j;
	}
	else
		j=next[j];
  }
}
int main()
{
	int x,y;
  while(~scanf("%s",a))
  {
	  if(a[0]=='.')
		  break;
   len=strlen(a);
   getnext();
   x=len-next[len];
   y=len%x;
   if(y==0)
	   printf("%d\n",len/x);
   else
	   printf("1\n");

  }
  return 0;
}


poj 2752  Seek the Name, Seek the Fame 字符串前缀子串等于后缀子串,输出前缀最后一个字母的位置。。next数组的运用。。。

#include<iostream>
#include<cstring>
using namespace std;
char a[400001];
int next[400001],len,ans[400001];
void getnext()
{
  int i,j;
  next[0]=-1;
  i=0;j=-1;
  while(i<len)
  {
    if(j==-1||a[i]==a[j])
	{
	  i++;
	  j++;
	  next[i]=j;
	}
	else
		j=next[j];
  }
}
int main()
{
	int i,j;
  while(~scanf("%s",a))
  {
    j=0;
   len=strlen(a);
   getnext();
   i=next[len];
   if(i!=0)
   ans[j++]=i;
   while(next[i]>0)
   {
     i=next[i];
	 ans[j++]=i;
   }
   for(i=j-1;i>=0;i--)
	   printf("%d ",ans[i]);
   printf("%d\n",len);
  }
  return 0;
}


暂时只做了这些很水的题目,多效的题现在对于自己还有点苦难,加油

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值