前言:
串的匹配算法广泛应用在现在的很多方面,这里讲一下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;
}
后记:
有什么更好的算法可以交流哦,谢谢。