弱鸡理解的kmp!

写写我对kmp的理解吧,kmp算法就是对于两个串的匹配,一个文本串S,一个模式串T,用T来匹配S
对于kmp算法的关键就是next数组的解读了,网上博客千万,各有观点吧,首先说一下我理解的next数组,next数组里面存入的值比如 next[3]=1;就是这个串abacdedf(比如说)next[3]就是代表aba这个串相同的前缀后缀,这里插入一下前缀后缀:
( 例如字符串:abcdab 它的前缀是,a,ab,abc,abcd,abcda; 它的后缀是:b,ab,dab,cdab,bcdab; 这个就是所谓的前缀和后缀)、
当模式串T与主串S匹配失败时候,模式串T返回到next [ j ](next [ j ] 代表下标为几,这里的字符串规定下标从0开始的)通过用next数组来减少没必要的匹配次数,模式串在下标为j 失配了,此时的next
[ j ]说的是下标,比如next[ 3 ]=1;就是返回到模式串下标为1的位置,也就是模式串的第二个,这里的next [ j ]的数值就是代表 文本串的 i(文本串是在i处失配的)文本串中 i 指针前的 next[ j ]位和模式串的0到 next [ j ]是完全相同的;
在这里我想说一下kmp这个模板

void kmp(int lena,int lenb)
{
	int i=0,j=0;
	 getnext(b);
	while(i<lena&&j<lenb)
	{
		if(j==-1||a[i]==b[j])
		{
			i++,j++;
		}
		else
		j=nextt[j];
		/**************************/到此不管结果匹配如何 此时的j是几就代表从模式串的开头匹配了几个字符,通俗来讲就是此时j是几就代表匹配了几个字符,例如如果模式串就第一个和文本串有一样的,不就是执行第一个if语句,j不就+1了,不就是从零到一了
		if(j==lenb)
		{
		  ********//内容
		}
	}
}

说了这么多,还是看题目吧;
**题目链接:**https://vjudge.net/contest/315202#problem/A

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=100005;
#define pi acos(-1.0)
using namespace std;
int n,m;
char nextt[100];
int total,lent;
char key[100];
void getnext(char *t)
{
	nextt[0]=-1;
	lent=strlen(t);
	int k=-1,j=0;
	while(j<lent)
	{
		if(k==-1||t[k]==t[j])
		  {
		  	k++,j++,nextt[j]=k;
		  }
		  else 
		  k=nextt[k];
	}
}
int main()
{
	sf("%d %d",&n,&m);
	 sf("%s",key);
	 getnext(key);
	 total=0;
	 int j=0;
	 while(total<m)
	 {
	 	printf("%c",key[j]);
	 	j++;
	 	if(j==n)
	 	{
	 		total++;
	 		j=nextt[n]; //为什么要返回next[n],next[n]是几,就代表模式串从下标为0开始到next[n]-1和文本串完全相同,也就是没用重复的了;
		 }
	 }
	return 0;
}

题目链接:https://vjudge.net/contest/315202#problem/B

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000005;
#define pi acos(-1.0)
using namespace std;
int t,n,m;
int a[maxn],b[maxn];
int ans,lena,lenb;
int  nextt[maxn];
void getnext(int *t)
{
	nextt[0]=-1;
	int k=-1,j=0;
	while(j<lenb)
	{
		if(k==-1||t[k]==t[j])
		{
			k++,j++,nextt[j]=k;
		}
		else
		 k=nextt[k];
	}
}
void kmp(int lena,int lenb)
{
	int i=0,j=0;
	 getnext(b);
	while(i<lena&&j<lenb)
	{
		if(j==-1||a[i]==b[j])
		{
			i++,j++;
		}
		else
		j=nextt[j];
		if(j==lenb)
		{
			ans=min(ans,i-j+1);
			//printf("yes\n");
			j=0;
		}
	}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
	   ans=inf;
	   scanf("%d %d",&lena,&lenb);
	   for(int i=0;i<lena;i++)
	   sf("%d",&a[i]);
	   for(int i=0;i<lenb;i++)
	   sf("%d",&b[i]);
	   kmp(lena,lenb);
	   if(ans!=inf)
	   printf("%d\n",ans);
	   else 
	   printf("-1\n");
	}
	return 0;
}

这个题目和第一个很像,就是返回 j=next[lent];
题目链接:https://vjudge.net/contest/315202#problem/C

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1e6+50;
#define pi acos(-1.0)
using namespace std;
int t,n,m;
char a[10005],b[1000005];
int ans,lena,lenb;
int  nexts[20005];
void getnext(char *p)
{
	int k=-1,j=0;
	nexts[0]=-1;
	while(j<strlen(p))
	{
		if(k==-1||p[k]==p[j])
		{
			k++,j++,nexts[j]=k;
		}
		else
		k=nexts[k];
	}
}
void kmp()
{
	int i=0,j=0,x=0;
	while(j<lena&&i<lenb)
	{
		if(j==-1||a[j]==b[i])
		{
			i++,j++;
		}
		else 
		 j=nexts[j];		
		 if(j==lena)
		 {	    
		 	ans++;		
		    j=nexts[j];		    
		 }		 
	}
}
int main()
{
	sf("%d",&t);
	while(t--)
	{	
		ans=0;
	   scanf("%s%s",a,b);//a是单词,b是文本
	   	lena=strlen(a);
		lenb=strlen(b);
	   getnext(a);	
	   	kmp();
    	printf("%d\n",ans);
	}
	return 0;
}

题目链接:https://vjudge.net/contest/315202#problem/D
这个题目不用返回next[lens],让j=0,比较简单;

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1005;
#define pi acos(-1.0)
using namespace std;
int  nextt[maxn];
char s[maxn],t[maxn];
int ans,lent,lens;
void getnext(char *t,int *nextt)
{
	nextt[0]=-1;
	int j=0,k=-1;
	while(j<lent)
	{
		if(k==-1||t[j]==t[k])
		{
			k++,j++;
			nextt[j]=k;
		}
		else
		k=nextt[k];
	}
}
void kmp(int lens,int lent)
{
	int i=0,j=0;
	while(i<lens&&j<lent)
	{
		if(j==-1||s[i]==t[j])
		{
			i++,j++;
		}
		 else
		 j=nextt[j];
	   if(j==lent)
	   {
		  ans++;
		  j=0;
	   } 
	} 
}
int main()
{
	while(sf("%s",s))
	{
		if(s[0]=='#') break;
		scanf("%s",t);
		lens=strlen(s),lent=strlen(t);
		ans=0;
		getnext(t,nextt);
		kmp(lens,lent);
		cout<<ans<<endl;
	}	
	return 0;
}

题目链接 :https://vjudge.net/contest/315202#problem/E
开始循环节了,循环节长度就是等于:下标-next[下标]; 下标从一开始

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
#define pi acos(-1.0)
using namespace std;
char key[maxn];
int nextt[maxn];
int n,k,lens,flag;
void getnext(char *p)
{
	int k=-1,j=0;
	nextt[0]=-1;
	lens=strlen(p);
	while(j<lens)
	{
		if(k==-1||p[k]==p[j])
		{
			k++,j++,nextt[j]=k;
		}
		else
		k=nextt[k];
	}
}
int main()
{
	flag=0;
	while(scanf("%d",&n)&&n)
	{
		sf("%s",key);
		printf("Test case #%d\n",++flag);
		memset(nextt,0,sizeof(nextt));
		getnext(key);
		for(int i=1;i<=n;i++)
		{
			if(!nextt[i])
			continue;
			k=i-nextt[i];
			if(i%k==0)
		    printf("%d %d\n",i,i/k);//前缀大小,循环次数 
		}
		printf("\n");		
	}
	return 0;
}

题目链接:https://vjudge.net/contest/315202#problem/G

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=100000+5;
#define pi acos(-1.0)
using namespace std;
int  nextt[maxn];
int t,lens;
char key[maxn];
void getnext()
{
	int j=0,k=-1;
	nextt[0]=-1;
	lens=strlen(key);
	while(j<lens)
	{
		if(k==-1||key[j]==key[k])
		{
			k++,j++,nextt[j]=k;
		}
		else k=nextt[k];
	}
 } 
int main()
{
	sf("%d",&t);
	while(t--)
	{
	   int ans;
	   lens=strlen(key);
	  sf("%s",key);
	  getnext();
	   int k=lens-nextt[lens];//循环结的长度 
	   if(k!=lens&&lens%k==0) ans=0;//防止abcde 
	   else
	   ans=k-nextt[lens]%k;//abcab去掉ab 
	   printf("%d\n",ans);	 
	}
	return 0;
}

题目链接:https://vjudge.net/contest/315202#problem/H

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
#define pi acos(-1.0)
using namespace std;
int nextt[maxn];
char s[maxn],ans[maxn],t[maxn];
int m,x,y,lens,lent,flag,xx;
void getnext()
{
	int k=-1,j=0;
	nextt[0]=-1;
	while(j<lent)              //(t[j])
	{
		if(k==-1||t[k]==t[j])
		{
			k++,j++,nextt[j]=k;
		}
		else
		k=nextt[k];
	}
}
void kmp()
{
   int i=0,j=0;
	while(i<y-x+1)
	{
		if(j==-1||ans[i]==t[j])
		{
			i++,j++;
		}
		else
		j=nextt[j];
		if(j==lent)
		{ 
		 //printf("yes\n");
		 xx++,j=nextt[j];
	    }
	}
} 
int main()
{
	scanf("%d %d %d",&lens,&lent,&m);
	sf("%s%s",s,t);
	while(m--)
	{
		xx=0;
		scanf("%d %d",&x,&y);
		x=x-1,y=y-1;
		if(y-x+1<lent)
		{
			cout<<0<<endl; continue;
		}
		flag=0;
		//memset(ans,0,sizeof(ans));
		for(int i=x;i<=y;i++)
		 ans[flag++]=s[i];
		 /*for(int i=0;i<=y-x;i++)
		 ans[i]=s[x-1+i];*/
		 getnext();
		 kmp();
		 printf("%d\n",xx);
	}
	 return 0;
}

拉马龙函数:返回一个字符串最大的回文字串。
题目链接:https://vjudge.net/contest/315202#problem/J

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
#define pi acos(-1.0)
using namespace std;
char begin[maxn<<1],end[maxn<<1];
int len[maxn<<1];
int turn()
{
	int lens=strlen(begin);
	int now=0;
	end[0]='$';
	for(int i=0;i<lens;i++)
	{
		end[++now]='#';
		end[++now]=begin[i];
	}
	end[++now]='#';
	return now;
}
int lamalong(int total)
{
	int maxx=0;
	int mx=0,id=0;
	for(int i=1;i<=total;i++)
	{
		if(i<mx)
		{
			len[i]=min(mx-i,len[2*id-i]);		
		}
		else 
		  len[i]=1;
		while(end[len[i]+i]==end[i-len[i]])
		len[i]++;
		if(i+len[i]>mx)
		{
			mx=i+len[i];
			id=i;
		}
		maxx=max(maxx,len[i]);
	}
	return maxx-1;
 } 
int main()
{
	int t=0;
	while(sf("%s",begin)!=EOF)
	{
		//if(begin[0]=='E') break;
		if(strcmp(begin,"END")==0) break;
		int total=turn();
		//cout<<total<<endl;
		int ans=lamalong(total);
		printf("Case %d: %d\n",++t,ans);
	}	 
	return  0;
}

题目链接:https://vjudge.net/contest/315202#problem/K

#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <string>
#define ll long long
#define inf 0x3f3f3f3f
#define sf scanf
#define min(x,y) x<y ? x : y
const int maxn=1000000+5;
//const int maxn=3*1e6+66;
#define pi acos(-1.0)
using namespace std;
char beginn[maxn<<1],endd[maxn<<1];
int len[maxn<<1];
int ans,t,lens;
int turnn()
{
	memset(endd,0,sizeof(endd));
	lens=strlen(beginn);
	int now=0;
	endd[0]='$';
	for(int i=0;i<lens;i++)
	{
		endd[++now]='#';
		endd[++now]=beginn[i];
	}
	 endd[++now]='#';
	// cout<<now<<endl; 
	 return  now;
}
int lamalong(int total)
{
	int maxx=0,mx=0,id=0;
	for(int i=1;i<=total;i++)
	{
		if(i<mx) len[i]=min(mx-i,len[2*id-i]);
		else len[i]=1;
		while(endd[len[i]+i]==endd[i-len[i]])
		len[i]++;
		if(i+len[i]>mx)
		{
			
			mx=i+len[i],id=i;
			//printf("此时 len[i]==%d id==%d,i==%d,mx==%d\n",len[i],id,i,mx);
		}
	}
	if(mx>=total) return len[id]-1;//除去本身 *q* 是2 mx左开右闭 
}
int main()
{
	int t,k=0;
	sf("%d",&t);
	while(t--)
	{
		scanf("%s",beginn);
		int total=turnn();
		lens=strlen(beginn);
		ans=lens+lens-lamalong(total);
		if(lens==lamalong(total))
		printf("Case %d: %d\n",++k,lens);
		else
		//printf("lamalong %d\n",lamalong(total));
		printf("Case %d: %d\n",++k,ans);
	}
	return 0;
 } 

对于字符串的循环节。不止是可以一维的,同时也可以是二维上的,比如一个二维数组让你去求,它的一个循环节,例如题目 poj2185 ;题目链接:https://vjudge.net/contest/315202#problem/M
思路就是求出每一维的循环节长度,然后取最大,结果相乘就是最小覆盖矩阵了。

#include <iostream>
using namespace std;
char s[10005][80];
int nextt[10005];
int r,c,l,h;
int getnextc(int row)
{
	int j=0,k=-1;
	nextt[0]=-1;
	while(j<c)//每行长度 
	{
		if(k==-1||s[row][j]==s[row][k])
		 j++,k++,nextt[j]=k;
		 else
		 k=nextt[k];
	}
	return  c-nextt[c];
}
int getnextr(int col)
{
	 int j=0,k=-1; nextt[0]=-1;
	while(j<r)//每列长度 
	{
		if(k==-1||s[j][col]==s[k][col])
		k++,j++,nextt[j]=k;
		else
		k=nextt[k];
	}
	return  r-nextt[r];
}
int main()
{
	while(scanf("%d %d",&r,&c)!=EOF)
	{
		for(int i=0;i<r;i++)
		scanf("%s",&s[i]);
			h=0,l=0;
		for(int i=0;i<r;i++)
		 h=max(h,getnextc(i));
		 for(int i=0;i<c;i++)
		 l=max(l,getnextr(i));
		 printf("%d\n",l*h);
	}
	return  0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值