字符串匹配KMP算法

所谓的字符串匹配是指在一个字符串S中找出另外一串字符串P的位置。

1 字符串匹配简单算法

字符串匹配简单算法是指,从正文S和模式P的第一个字符出发,将S和P的字符依次逐个进行比较,如果模式P中所有的字符都与S中的字符串匹配完,则说明在正文S中找到了模式P的字符串,返回位置就可以了,如果没有匹配完成,则将模式P沿着正文S向后移了一个位置,再从模式P的第一个字符开始以此进行比较,如果没有找到则返回-1。
例如正文S为“ABABABCCA”,P为“ABABC”.具体实现代码如下:
#include <iostream>
#include<string>
using namespace std;

int fstr(string ,string);
int main()
{
	string str1,str2;
	cout<<"请输入正文S:";
	cin>>str1;
	cout<<"请输入模式P:";
	cin>>str2;
	cout<<fstr(str1,str2);
	return 0;
}

int fstr(string S ,string P)
{
	int m=S.size();
	int n=P.size();
	int len=0;
	int i=0,j=0;
	for (len=0;len<=m-n;len++)
	{
		i=len;
		j=0;
		while((i<m)&&(j<n)&&(S[i]==P[j]))
		{
			++i;++j;
		}
		if (j==n)
		{
			return len+1;
		}
	}
	return -1;
}

2 字符串匹配KMP算法

KMP算法是指,在字符串匹配的过程中,如果发现正文S与模式P中有字符不匹配了,找到一种模式P沿正文S向后移动的规则,以便使得正文S中失去匹配的字符以前的字符不再参与比较,即只从当前失去匹配的字符开始与模式P中的字符继续进行比较。例如如图所示:

发现模式P中第7个字符C与正文S的第7个字符X不相符时,将模式P沿着正文S 向后移动2个字符单位,接着比较模式P中的第5个字符与正文S中的第7个字符是否相等。为什么可以这样呢?因为很明显的发现模式P的第3-6个字符与其第1-4个字符是相同的,如下图所示。

于是我们可以定义一个这样的函数,针对需要匹配的字符串,j=F(i),其中i表示当第i个字符不匹配时,j表示我们需要退回到从第j个字符开始比较,例如上例中的i就表示的是7,j就表示的是5;规定的是1=F(1),用递推的规律求F(i);假如前面的F(1)=1~F(i-1)=j均已求出,现在来求F(i);

如图所示,当F(i-1)=j表示P[1]~P[j-1]与P[i-j]~P[i-2]是对应相等的,当P[i-1]与P[j]也相等时,很明显F(i)=F(i-1)+1;,若P[i-1]与P[j]不相等时,则需要退一步从F(j)=k来考虑;

考虑当P[i-1]与P[k] 相等时,很明显F(i)=F(k )+1;,若P[i-1]与P[k ]不相等时,则需要退一步从F(k )=x 来考虑;只要K大于1,当k退回为1时,则令 F(i)= 1;重复上面的步骤,找到F(i);根据上面的算法可以得出模式P等于“ABABABCCA”时,对应的回溯位置为F(1)=1;F(2)=1;F(3)=1;F(4)=2;F(5)=3;F(6)=4;F(7)=5;F(8)=1;F(9)=1;具体的代码如下:
#include <stdio.h> 
#include <string.h>
#include <stdlib.h> 

int pskmp(char s[],char p[]);
void pflink(char p[],int *flink);
int main()
{
	char s[]="ACCAABCCA";
	char p[]="BCCA";
	int n=pskmp(s,p);
	printf("%d",n);
	return 0;
}
int pskmp(char s[],char p[])
{
	int m=strlen(s);
	int n=strlen(p);
	int i=0,j=0,flag=0;
	int* flink;
	flink=(int *)malloc(n*sizeof(int));
	pflink(p,flink);
	while(i<m&&j<n&&flag==0)
	{
		while (j>0&&(p[j]!=s[i])) j=flink[j];
		if (p[j]!=s[i])//由于模式P的第一个位置就与正文S不匹配时,则直接向右移动一个
			i++;
	    if (j==n-1) flag=1;
		else{i++;j++;}
	}
	if (flag==1) i=i-n+2;
	else i=-1;
	free(flink);
	return i;
}
void pflink(char p[],int *flink)
{
	int n=strlen(p),i=2,j;
	flink[0]=0,flink[1]=0;//由于数组的起始位置为0,所以第一个位置也由1变为0
	while (i>0&&i<n)
	{
		j=flink[i-1];
		while (j>0&&(p[i-1]!=p[j])) j=flink[j]; 
		if (p[i-1]==p[j])	//由于数组的起始位置为0,为了找到第一个1,所以必须再次判断
		{
			flink[i]=j+1;
		}
		else flink[i]=0; //当未找到匹配时直接从第一个位置0开始
		i++;
	}	
}









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值