病毒感染检测:串的模式匹配 BF、KMP算法

在网络入侵检测、计算机病毒特征码匹配以及DNA序列匹配等应用中都需要进行串的模式匹配。针对这个问题,书上讲了两种模式匹配的算法,即BF算法和KMP算法,下面针对这两种算法的实现谈谈我的思路。

1、代码部分

BF算法

#include<iostream>
using namespace std;
#include<string.h>//采用静态顺序存储结构(定长)
typedef struct{char ch[1000001];    //存储串的一维数组 int length;      //串的长度
}SString;SString S,T; char s[1000001];char t[1000001];//BF算法
//查找 模式T 在 主串S 中第pos个字符开始第一次出现的位置,并返回
//若不存在,则返回0 (T非空,1<=pos<=S.length)
int Index_BF(SString S,SString T,int pos)
{               int i,j;i=pos;j=0;while(i<=S.length-1 && j<=T.length-1){if(S.ch[i]==T.ch[j]){   //从各自的第一位开始比较,如果相同,比较下一位 ++i;++j;}else {//如果不同,主串指针回到 上次开始比较时的字符 的下一个字符,//模式回到第一个字符,重新开始比较 i=i-j+1;j=0;} }if(j>T.length-1)     //匹配成功 return i-T.length+1;//主串指针位置往回退模式长度个单位,就回到了该模式在主串中第一次出现的位置 else          //匹配失败 return 0;    //返回0(顺序存储的字符串是从下标为1的数组分量开始存储的,下标为0的分量闲置不用)
} //主函数
int main()
{cin>>s>>t;strcpy(S.ch,s);strcpy(T.ch,t);S.length=strlen(S.ch);T.length=strlen(T.ch);cout<<Index_BF(S,T,0)<<endl;;return 0;
}

KMP算法

#include<iostream>
using namespace std;
#include<string.h>//采用静态顺序存储结构(定长)
typedef struct{char ch[1000002];    //存储串的一维数组 int length;      //串的长度
}SString;SString S,T; char s[1000002];char t[1000002];int nex[1000002];//KMP算法
//查找 模式T 在 主串S 中第pos个字符开始第一次出现的位置,并返回
//若不存在,则返回0 (T非空,1<=pos<=S.length)
int Index_KMP(SString S,SString T,int next[])
{               int i,j;i=j=0;while(i<=S.length-1 && j<=T.length-1){if(j==-1||S.ch[i]==T.ch[j]){  //从各自的第一位开始比较,如果相同,比较下一位 ++i;++j;}else {j=next[j];} }if(j>T.length-1)     //匹配成功 return i-T.length+1;// else         //匹配失败 return 0;
} void get_next(SString T,int next[]){int i=0;next[0]=-1;int j=-1;while(i<T.length-1){if(j==-1||T.ch[i]==T.ch[j]){++i;//前缀开始的位置++j;//后缀开始的位置next[i]=j;//next[]是我的子串和主串的那一位对着的位置,现在把后缀开始的位置值赋给他。}else {j=next[j];}}}
//主函数
int main()
{cin>>s>>t;strcpy(S.ch,s);strcpy(T.ch,t);S.length=strlen(S.ch);T.length=strlen(T.ch);get_next(T,nex);cout<<Index_KMP(S,T,nex)<<endl;return 0;
}

病毒感染检测

#include<iostream>
#include<string>using namespace std;int Index_BF(string, string, int); //匹配则返回开始下标,否则返回-1
int Virus_detecion(string, string); //感染则返回1,否则返回0int main()
{string s,t;cin >> s >> t;if(Virus_detecion(s, t)==1) cout << "YES" << endl;else cout << "NO" << endl;return 0;
}int Index_BF(string s, string t, int pos)
{//返回模式t在主串s中第pos个字符开始第一次出现的位置下标。
//若不存在,则返回值为-1 //其中,t非空,1≤pos≤StrLength(s) int i,j;i = pos-1; //下标 j = 0; //下标 while(i<s.length() && j<t.length()){ if(s[i]==t[j]){++i; ++j;}    //继续比较后继字符 else{i=i-j+1; j=0;}   //指针后退重新开始匹配}   if(j==t.length()) return i-t.length(); //模式串全部读完,表示匹配,返回开始匹配位置的下标 else return -1;
} int Virus_detecion(string s, string t)
{//求病毒DNA(环状)在人DNA中是否出现,有则返回在人DNA中第几个字符出现,无则返回0 int num = t.length();t += t; //模式串生成两次   string temp; int res;for (int i=0; i<num; i++){temp.assign(t, i, num);        //cout << temp << endl;res = Index_BF(s, temp, 1) ; //调用 Index_BF 检测本次的num个字符是否与s匹配 if(res!=-1)  return 1; // 本次匹配,说明感染病毒 }return 0; //所有检测都不匹配,说明没有感染病毒
}

2、探索过程

一、BF算法

【错误代码1】

#include<iostream>
#include<string.h>
using namespace std;typedef struct{char ch[1000002];int length;}SString;void  Index_BF(SString S, SString T, int pos){ int i,j;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 )   {cout<< i-T.length<<endl;}else{ cout<<"0"<<endl;}
}
}
int main()
{SString S,T;char s[1000002]={0},t[1000002]={0};cin>>s>>t;strcpy(S.ch,s);strcpy(T.ch,t);Index_BF(S, T , 1);return 0;
}

分析:编译轻松通过,但是怎么都不能输入进去。参考其他同学的博客,发现我的这个问题有人也遇到过,原因在于在主函数中定义了两个长度为100W的数组,程序根本跑不动。
解决:参考博客发现有两种途径解决数组过大问题:①将数组定义为全局变量 ②动态分配数组
针对我这一题的具体情况,因为我是要用到strcpy函数来copy数组的内容的,所以第一种方式显然更适合。


【错误代码2】

#include<iostream>
#include<string.h>
using namespace std;typedef struct{char ch[1000002];int length;}SString;char s[1000002]={0},t[1000002]={0};SString S,T;void  Index_BF(SString S, SString T, int pos){ int i,j;i=pos;j=1;cout<<"smooth"<<endl;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 )   {cout<< i-T.length<<endl;}else{ cout<<"0"<<endl;}
}
}
int main()
{cin>>s>>t;cout<<"good"<<endl;strcpy(S.ch,s);cout<<"OK"<<endl;strcpy(T.ch,t);cout<<"OK"<<endl;Index_BF(S, T , 1);return 0;
}

分析:这一次程序是顺利运行完了,就是没有想要的结果出来。
解决:为了查找程序在哪里断掉了,我在一些步骤之后设置了一些输出,比如cout<<“good”。此法说明我的问题出在BF算法上。经过仔仔细细的检查,发现BF算法中我打while循环的时候掉了一个括号,加上去就正常了。
接下来需要解决位置和下标的问题,我现在输出的还是下标,不是题目要求的位置。思考过后发现解决的途径有两种:改数组;改算法。这里我选择的是改算法的方法。


【正确程序1】

#include<iostream>
using namespace std;
#include<string.h>//采用静态顺序存储结构(定长)
typedef struct{char ch[1000001];    //存储串的一维数组 int length;      //串的长度
}SString;SString S,T; char s[1000001];char t[1000001];//BF算法
//查找 模式T 在 主串S 中第pos个字符开始第一次出现的位置,并返回
//若不存在,则返回0 (T非空,1<=pos<=S.length)
int Index_BF(SString S,SString T,int pos)
{               int i,j;i=pos;j=0;while(i<=S.length-1 && j<=T.length-1){if(S.ch[i]==T.ch[j]){   //从各自的第一位开始比较,如果相同,比较下一位 ++i;++j;}else {//如果不同,主串指针回到 上次开始比较时的字符 的下一个字符,//模式回到第一个字符,重新开始比较 i=i-j+1;j=0;} }if(j>T.length-1)     //匹配成功 return i-T.length+1;//主串指针位置往回退模式长度个单位,就回到了该模式在主串中第一次出现的位置 else          //匹配失败 return 0;    //返回0(顺序存储的字符串是从下标为1的数组分量开始存储的,下标为0的分量闲置不用)
} //主函数
int main()
{cin>>s>>t;strcpy(S.ch,s);strcpy(T.ch,t);S.length=strlen(S.ch);T.length=strlen(T.ch);cout<<Index_BF(S,T,0)<<endl;;return 0;
}

现在把程序放到PTA上面跑,得到了15分,唯一错误的一个点就是超时。但这个是BF算法无法解决的了,所以我决定一鼓作气把他改成KMP算法。

二、KMP算法的探索

在真正着手写代码前,我花了很久阅读课本,也想了很久,终于弄明白了这个算法的核心思想。以下是我的思路:

①BF究竟是哪里麻烦了?

设想一种情况:主串是abcab... 子串是abcac... 如果按照BF算法我们知道,一旦b和c不匹配了,接下来比较的就是主串的第二个字符b和子串的第一个字符a,不匹配时主串和子串都右移。直到子串的首位字符a与主串的a对齐之前,我们做的都是无用功。这就是从一个具体的例子看出来的麻烦之处。

②如果说KMP可以跳过这些“无用功”,那什么情况下可以跳?这是一个特殊情况,真的能上升到一个算法的高度吗?

为了解决我以上的疑虑,我决定用抽象的模型进行说明。

设主串Si(大家不要把它看成硅元素的元素符号啦)与模式tj失配……这句话暗含(S1S2……Si-1与t1t2……tj-1是匹配的) 这里我们将主串Si固定死,让他与模式中的tk匹配(因为不知道,所以设k这个未知数,也就是说这时候模式要从j跳到k)即:

Si-k+1...Si-1 =t1...tk-1

我们由第一步的匹配就已经得到:

Si-k+1...Si-1 = tj-k+1...tj-1

把这两个式子联立,得到:

t1...tk-1 = tj-k+1...tj-1

③求k

以上我们得到了一个很重要的关系式,即t1...tk-1 = tj-k+1...tj-1,它隐含了我们想要知道的k,也就是子串该跳到哪一个地方这一信息。

观察式子,我们首先考虑一些边边角角的问题,比如k=1,j=1这些情况。

1)k=1 这时式子变成了t1...t0 = tj...tj-1 这种情况说明子串不符合要求,没有那个相等的部分。顺便也解决了我们第二问中的疑惑,其实KMP算法的优化是有条件的,要求子串与主串匹配的那部分有“相等的部分”。 这时候的处理办法也只有老老实实跳到t1与Si比较。模式啊模式!你若是堕落,KMP也救不了你!

2)j=1 其实就是模式的第一位就与主串Si不匹配。那还犹豫啥?直接把模式向右移一个呗。

④怎么用算法实现?

其实整个大框架在BF算法的基础上来说不用怎么改,只需要把回溯的那块儿稍微修改一下,即改成主串i不回溯,模式跳到k位置。

但是现在说来说去我们还是只有一串关系式,还有两个边界的情况,k到底怎么表示?对解决这个问题还是一脸懵逼。

阅读课本发现他使用一个next[j]函数来表示子串下一个回溯的位置。 其实想一想next[j]仅与模式有关,和主串半毛钱关系也没有。从已知的关系式t1...tk-1 = tj-k+1...tj-1入手,此时next[j] = k。我们求一求next[j+1]?这时候产生两种情况需要讨论:

1)若tk = tj 那么t1...tk = tj-k+1...tj 也就是next[j+1] = k+1 , 亦即next[j+1] = next[j] +1。

2)若tk!=tj 是不是有一种似曾相识的感觉?对了,这又是一次模式匹配,只不过此时的模式既充当了主串又充当模式。所以我们要把模式中的第next[j]个字符和主串中的第j个字符对齐进行比较。这种情况可以利用函数的递归调用,让j=next[j]。


【正确程序2】

#include<iostream>
using namespace std;
#include<string.h>//采用静态顺序存储结构(定长)
typedef struct{char ch[1000002];    //存储串的一维数组 int length;      //串的长度
}SString;SString S,T; char s[1000002];char t[1000002];int nex[1000002];//KMP算法
//查找 模式T 在 主串S 中第pos个字符开始第一次出现的位置,并返回
//若不存在,则返回0 (T非空,1<=pos<=S.length)
int Index_KMP(SString S,SString T,int next[])
{               int i,j;i=j=0;while(i<=S.length-1 && j<=T.length-1){if(j==-1||S.ch[i]==T.ch[j]){  //从各自的第一位开始比较,如果相同,比较下一位 ++i;++j;}else {j=next[j];} }if(j>T.length-1)     //匹配成功 return i-T.length+1;// else         //匹配失败 return 0;
} void get_next(SString T,int next[]){int i=0;next[0]=-1;int j=-1;while(i<T.length-1){if(j==-1||T.ch[i]==T.ch[j]){++i;//前缀开始的位置++j;//后缀开始的位置next[i]=j;//next[]是我的子串和主串的那一位对着的位置,现在把后缀开始的位置值赋给他。}else {j=next[j];}}}
//主函数
int main()
{cin>>s>>t;strcpy(S.ch,s);strcpy(T.ch,t);S.length=strlen(S.ch);T.length=strlen(T.ch);get_next(T,nex);cout<<Index_KMP(S,T,nex)<<endl;return 0;
}

三、病毒感染检测

这个问题要处理的操作对象就是字符串,将病毒的DNA序列看作是子串,患者的DNA序列看作是主串,检测任务的实质就是看子串是否在主串中出现过。一定要注意病毒的DNA序列是环状的。而且具体问题中往往不会在输入的窗口中将序列一个个敲上去,往往会将待检测的数据存储到一个文本文件中,输出也以一个文件的形式输出。

由于文件无法直接上传,请大家自行建立一个名为“病毒感染检测输入数据.txt”的文件,内容如下:

11
baa bbaabbba
baa aaabbbba
aabb abceaabb
aabb abaabcea
abcd cdabbbab
abcd cabbbbab
abcde bcdedbda
acc bdedbcda
cde cdcdcdec
cced cdccdcce
bcd aabccdxdxbxa

输出应该是:

baa bbaabbba YES
baa aaabbbba YES
aabb abceaabb YES
aabb abaabcea YES
abcd cdabbbab YES
abcd cabbbbab NO
abcde bcdedbda NO
acc bdedbcda NO
cde cdcdcdec YES
cced cdccdcce YES
bcd aabccdxdxbxa NO

从病毒感染检测谈串的模式匹配相关推荐

  1. 数据结构——基于字符串模式匹配算法的病毒感染检测

    实验四 基于字符串模式匹配算法的病毒感染检测 [实验目的] 1.掌握字符串的顺序存储表示方法. 2.掌握字符串模式匹配BF算法和KMP算法的实现. [实验内容] 问题描述 医学研究者最近发现了某些新病 ...

  2. (C语言)数据结构算法-病毒感染检测(BF算法KMP算法)

    病毒感染检测: 医学研究者最近发现了某些新病毒,得知它们的DNA序列都是环状的.为了快速检测出患者是否感染了相应的病毒,研究者将患者的DNA和病毒的DNA均表示成一些字母组成的字符串序列,然后检测某种 ...

  3. PTA 病毒感染检测 (15分)

    病毒感染检测 (15分) 作者:王东 单位:贵州师范大学 代码长度限制:16 KB 时间限制:400 ms 内存限制:64 MB 人的DNA和病毒DNA均表示成由一些字母组成的字符串序列.然后检测某种 ...

  4. 7-8 病毒感染检测 (10分)

    7-8 病毒感染检测 (10分) 人的DNA和病毒DNA均表示成由一些字母组成的字符串序列.然后检测某种病毒DNA序列是否在患者的DNA序列中出现过,如果出现过,则此人感染了该病毒,否则没有感染.例如 ...

  5. 7-4 病毒感染检测 (15 分)

    7-4 病毒感染检测 (15 分) 人的DNA和病毒DNA均表示成由一些字母组成的字符串序列.然后检测某种病毒DNA序列是否在患者的DNA序列中出现过,如果出现过,则此人感染了该病毒,否则没有感染.例 ...

  6. 算法复现 - 病毒感染检测(案例4.1算法4.5)

    第1关:案例4_1 病毒感染检测 #include <iostream> #define maxsiz 20 #include<string.h> using namespac ...

  7. 数据据结构实验4《基于字符串模式匹配算法的病毒感染检测》

    (visual studio 2019可运行) 输入及输出要求见<数据结构C语言(第二版)>严蔚敏版 [本文仅用于啥都看不懂还想交作业选手] //KMP求解基于字符串模式匹配算法的病毒感染 ...

  8. 数据结构之串:串的模式匹配

    串:串的模式匹配 模式匹配的定义: 实现代码: 模式匹配的定义: 实现代码: int Index(SString S,SString T,int pos){int i = pos,j = 1;whil ...

  9. 串的模式匹配(KMP算法)

    [问题描述] 串的模式匹配算法实现(KMP算法) [输入形式] 第一行输入主串s: 第二行输入模式串t: 第三行输入起始位置pos: [输出形式] 输出模式串t的next值(以空格分隔) 输出模式匹配 ...

最新文章

  1. 计算机调查取证分析篇
  2. [翻译]Global Descriptor Table-GDT
  3. Vue实现仿音乐播放器12-实现歌手页面效果
  4. 控制台打印三角形、菱形
  5. 【报告分享】罗兰贝格2019年关于人工智能的十个议题.pdf(附下载链接)
  6. Python之网络编程(一)
  7. GPS经纬度转百度经纬度JAVA
  8. 时间序列复杂性的度量—近似熵和样本熵
  9. 利用C语言编程输出小写英文字母表的大写形式(以5为间隔)
  10. The Top 5 cloud security threats presented by Mark Russinovich
  11. oracle左连接应用场景,Oracle左连接left join 的实际操作与应用
  12. 《Learning for Disparity Estimation through Feature Constancy》
  13. 人生法则:蝴蝶效应、青蛙现象、鳄鱼法则、鲇鱼效应、羊群效应、刺猬法则...
  14. 金沙滩51单片机74HC138 三八译码器的应用
  15. 知识图谱系统课程笔记(二)——知识抽取与挖掘
  16. STEP7 V5.5 SP4 HF1 + WINCC7.2 + PLCSIM V5.4 SP5工程项目如何进行PLC程序和WINCC画面仿真运行
  17. 用hadoop计算PI值
  18. SpringBoot 事务管理
  19. 腾讯技术解读|CDG—金融科技和腾讯广告AMS的神秘武器
  20. VBScript之Windows 脚本宿主

热门文章

  1. 1.5远程控制及文件传输
  2. 触摸屏同时通讯多个变频器,修改频率等参数,启动正反转停止
  3. Stata中可以用数字作为local吗
  4. 陈二狗的妖孽人生经典语录
  5. 铸造IT核心技术的国之重器,YottaChain掀起存储行业时代变革
  6. 小度音箱对接之DuerOS开放平台智能家居技能协议分析
  7. SSL/TLS证书过期了怎么办?
  8. 骨头收藏家-01背包
  9. Xshell 7下载和XFTP 7下载
  10. 蜗牛星际b款装服务器系统,记录下蜗牛星际更换系统硬盘重新安装系统的一番折腾...