数据结构KMP&BF算法

前言:

串的匹配算法广泛应用在现在的很多方面,这里讲一下BF算法和KMP算法(重点)。

一、BF算法,其原理就是循环进行比较,字符相同则下标都加一,不同主串回到i+1,子串回到1,重新匹配。

算法如下:

/*BF算法*/
int Index_BF(SString S,SString T,int pos)
{//返回模式T在主串S中第pos个字符开始第一次出现的位置,若不存在则返回值为0
//其中,T非空,1<=pos<=S.length
	i=pos;j=1;
	while(i<=S.length && j<=T.length)
	{
		if(S.ch[i]==T.ch[j]{++i;++j;}
		else{i=i-j+2;j=1;}
	}
	if(j>T.length) return i-T.length;
	else return 0;
}

算法时间复杂度O(m*n)

二、KMP算法,重点讲解。

这种算法是由Knuth、Morris和Pratt同时设计实现的,因此简称KMP算法。(Donald Ervin Knuth因此算法获得 1974年图灵奖,大佬!)
利用已经部分匹配的结果而加快模式串的滑动速度,且主串S的指针i不必回溯!可将算法提速到O(n+m)!
如图所示,子串匹配情况
满足的子串要有如图的形式:
在这里插入图片描述
那么next函数如下:
在这里插入图片描述
举个例子就有:
求next值k示例

T=“abcaabbabcab”
j 1 2 3 4 5 6 7 8 9 10 11 12
模式串 a b c a a b b a b c a b

next[j] 0 1 1 1 2 2 3 1 2 3 4 5

所以KMP算法有:

/*KMP算法*/
int Index_KMP(SString S, SString T,int pos)
{//利用模式串T的next函数求T在主串S中第pos个字符后的位置
	//其中,T非空,1<=pos<=S.length
	i=pos;j=1;
	while(i<=s.length&&j<=T.length)//两个串均未比较到串尾
	{
		if(j==0||S.ch[i]==T.ch[j]){++i;++j;}//继续比较后续字符
			else j=next[j];//模式串向右移动
	}
		if(j>T.length) return i-T.length;//匹配成功
		else return 0;//匹配失败
	}
}

求next函数的算法有:

/*计算next函数值*/
void get_next(SString T,int next[])
{
	//求模式串T的next函数值并存入数组next
	i=1;next[1]=0;j=0;
	while(i<T.length)
	{
		if(j==0||T.ch[i]==T.ch[j]){++i;++j;next[i]=j;}
		else j=next[j];
	}
}

你会发现上面的next函数是有缺陷的,例如模式串中的1~3个字符和第四个字符都相等的时候,不需要在和主串中的第四个字符进行比较,可以将模式连续向右滑动4个字符的位置进行i=5,j=1时的字符进行比较。
j 1 2 3 4 5
模式串 a a a a b
next[j] 0 1 2 3 4
nextval[j] 0 0 0 0 4

求next函数修正值算法描述:

/*计算next的修正值*/
void get_nextval(SString T,int nextval[])
{
	//求模式串T的next函数修正值并存入数组nextval
	i=1;nextval[1]=0;j=0;
	while(i<T.length)
	{
		if(j==0||T.ch[i]==T.ch[j])
		{
			++i;++j;
				if(T.ch[i]!=T.ch[j]) nextval[i]=j;
				else nextval[i]=nextvall[j];
		}
		else j=nextval[j];
	}
}

另附病毒检测的BF算法代码:

#include<iostream>
#include<stdlib.h>
#include<string>
using namespace std;
/*BF算法*/
int IndexBF(string S,string Temp)
{//返回模式T在主串S中第pos个字符开始第一次出现的位置,若不存在则返回值为0
//其中,T非空,1<=pos<=S.length
	int i=0;int j=0;
	while(i<S.length()&& j<Temp.length())
	{
		if(S[i]==Temp[j]){++i;++j;}
		else{i=i-j+1;j=0;}

	}
	if(j==Temp.length()) return 1;
	else return 0;
}

/*病毒感染检测*/
void Virus_detection(string S,string T)
{
	int i;
	int flag = 0;
	int m = T.length();
	T += T;
	string temp;
	for(i=0;i<m;i++)
	{
		temp = T.substr(i,m);
		flag = Index_BF(S,temp);
		if(flag) break;
	}
	if(flag) cout<<"YES!"<<endl;
	else cout<<"NO!"<<endl;
}

int main()
{
	string S,T;
	cout<<"请输入人的DNA:"<<endl;
	cin>>S;
	cout<<"请输入病毒的DNA:"<<endl;
	cin>>T;
	Virus_detection(S,T);
	return 0;
}

//KMP算法的代码:

#include<iostream>
#include<stdlib.h>
#include<string>
#include<fstream>
using namespace std;

typedef struct{   
   char ch[600]; //若是非空串,则按串长分配存储区,否则ch为NULL   
   int len; //串长度   
}HString; 

int* GetNextVal(const char *s, int len)
{
 
    int *next = new int[len];
    int i = 1;
    int j = 0;
    next[1] = 0;
    while(i<len)
    {
 if(j==0 || s[i]==s[j])
 {
   ++i;
  ++j;
  next[i] = j;
  }
  else
{
j = next[j]; 
 }
    }
    return next;
}

int KMP(const char *s, int slen,const char *t,int tlen)
{
  
    int i,j;
    int *next = GetNextVal(t, tlen);
   
    i = 1;
    j = 1;
    while(i<=slen && j<=tlen)
    {
 if(j==0 || s[i]==t[j])
   {
    ++i;
   ++j;
   }
    else
   {
   j = next[j];
   }
}

    if(j>tlen)
    return i-tlen;
    return 0;
}

void Virus_detection()
{ 
   int num,m,flag,i,j; char Vir[600];
   HString Virus,Person,temp;
   ifstream inFile("病毒感染检测输入数据.txt");
   ofstream outFile("病毒感染检测输出结果.txt");
   inFile>>num;//读取待检测的任务数
   while(num--) //依次检测每对病毒DNA和人的DNA是否匹配
   {
inFile>>Virus.ch+1;//读取病毒DNA序列
inFile>>Person.ch+1;//读取人的DNA序列
strcpy(Vir,Virus.ch);
 Virus.len=strlen(Virus.ch)-1;
 Person.len=strlen(Person.ch)-1;
 flag=0;//用来标识是否匹配,初始为0,匹配后为非0
 m=Virus.len;
 for(i=m+1,j=1;j<=m;j++) Virus.ch[i++]=Virus.ch[j];
 //因病毒为环状,故将病毒的长度扩大2倍
//即可线性取到所有长度为Virus.len的字符串
Virus.ch[2*m+1]='\0'; //添加结束符号
for(i=0;i<m;i++)
{
for(j=1;j<=m;j++) temp.ch[j]=Virus.ch[i+j];
 //取长为Virus.len的环形字符串
//即Virus.ch[0]– Virus.ch[Virus.len-1],
 //Virus.ch[1]– Virus.ch[0], Virus.ch[2]– Virus.ch[1]…
   temp.ch[m+1]='\0'; //添加结束符号
temp.len=strlen(temp.ch)-1;
         
flag=KMP(Person.ch,Person.len,temp.ch,temp.len);  //模式匹配

   if(flag) break; //匹配即可退出循环
 }//for
   if(flag)
 outFile<<Vir+1<<"   "<<Person.ch+1<<"   "<<"YES"<<endl;
else
outFile<<Vir+1<<"   "<<Person.ch+1<<"   "<<"NO"<<endl;
   }//while
}

int main()
{
Virus_detection();
return 0;
}

后记:

有什么更好的算法可以交流哦,谢谢。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是C语言数据结构BF算法和KMP算法的介绍: 1. BF算法(暴力匹配算法) BF算法是一种朴素的字符串匹配算法,也称为暴力匹配算法。它的基本思想是从主串的第一个字符开始,依次与模式串的每个字符进行比较,如果匹配成功,则继续比较下一个字符,否则主串指针后移一位,重新开始匹配。这种算法的时间复杂度为O(m*n),其中m和n分别为主串和模式串的长度。 以下是BF算法的C语言实现: ```c #include <stdio.h> #include <string.h> int BF(char *s, char *p) { int i = 0, j = 0; int slen = strlen(s); int plen = strlen(p); while (i < slen && j < plen) { if (s[i] == p[j]) { i++; j++; } else { i = i - j + 1; j = 0; } } if (j == plen) { return i - j; } else { return -1; } } int main() { char s[] = "hello world"; char p[] = "world"; int pos = BF(s, p); if (pos != -1) { printf("匹配成功,位置为:%d\n", pos); } else { printf("匹配失败\n"); } return 0; } ``` 2. KMP算法(Knuth-Morris-Pratt算法) KMP算法是一种改进的字符串匹配算法,它的基本思想是利用已知信息来避免无效的比较。具体来说,它通过预处理模式串,得到一个next数组,用于指导匹配过程中的跳转。在匹配过程中,如果当前字符匹配失败,则根据next数组的值进行跳转,而不是直接从主串的下一个字符开始匹配。这种算法的时间复杂度为O(m+n),其中m和n分别为主串和模式串的长度。 以下是KMP算法的C语言实现: ```c #include <stdio.h> #include <string.h> void getNext(char *p, int *next) { int i = 0, j = -1; int plen = strlen(p); next[0] = -1; while (i < plen - 1) { if (j == -1 || p[i] == p[j]) { i++; j++; next[i] = j; } else { j = next[j]; } } } int KMP(char *s, char *p, int *next) { int i = 0, j = 0; int slen = strlen(s); int plen = strlen(p); while (i < slen && j < plen) { if (j == -1 || s[i] == p[j]) { i++; j++; } else { j = next[j]; } } if (j == plen) { return i - j; } else { return -1; } } int main() { char s[] = "hello world"; char p[] = "world"; int next[strlen(p)]; getNext(p, next); int pos = KMP(s, p, next); if (pos != -1) { printf("匹配成功,位置为:%d\n", pos); } else { printf("匹配失败\n"); } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

threecat.up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值