第1关:基于BF算法的病毒感染监测
任务描述
医学研究者最近发现了某些新病毒,通过对这些病毒的分析,得知它们的DNA序列都是环状的。现在研究者收集了大量的病毒DNA和人的DNA数据,想快速检测出这些人是否感染了相应的病毒。为方便研究,研究者将人的DNA和病毒的DNA均表示成由一些小写字母组成的字符串,然后检测某种病毒的DNA序列是否在患者的DNA序列中出现过,如果出现过,则此人感染了病毒,否则没有感染。注意:人的DNA序列是线性的,而病毒的DNA序列是环状的。请使用BF算法检测人是否感染相应病毒。
编程要求
输入
多组数据,每组数据有一行,为序列A和B,A对应病毒的DNA序列,B对应人的DNA序列。A和B都为“0”时输入结束。
输出
对于每组数据输出一行,若患者感染了病毒输出“YES”,否则输出“NO”。
#include<iostream>
#include<cstring>
#define MAXSIZE 1000
using namespace std;
int BF(char a[],char b[])
{//简单模式匹配算法,匹配成功返回1,否则返回0
/**************begin************/
int L1 = strlen(a);
int L2 = strlen(b);
for(int i=0,j=0;i<L2;){
if(b[i] == a[j]){
i++;
j++;
}else{
i = i-j+1;
j = 0;
}
if(j > L1-1) return 1;
}
return 0;
/**************end************/
}
void Revolve(char a[])
{//字符串旋转,把病毒第一个字符变为最后一个字符
/**************begin************/
int L1 = strlen(a);
char temp = a[0];
for(int i = 0;i < L1 - 1;i++){
a[i] = a[i+1];
}
a[L1-1] = temp;
/**************end************/
}
int Judge(char a[],char b[])
{//判别病毒DNA环状序列是否在患者DNA序列中出现过,出现过返回1,否则返回0
/**************begin************/
int L1 = strlen(a);
while(L1--){
int re = BF(a,b);
if(re == 1) return 1;
Revolve(a);
}
return 0;
/**************end************/
}
int main()
{
char a[MAXSIZE],b[MAXSIZE];//a存入病毒的DNA序列,b存入人的DNA序列
while(cin>>a>>b)
{
if(strcmp(a,"0")==0&&strcmp(b,"0")==0)
break;
int result=Judge(a,b);
if(result)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
第2关:基于BF算法的网络入侵检测
任务描述
随着互联网的飞速发展,网络安全问题日益严重。入侵检测技术是一种积极主动防御的安全保障技术,而Snort是其中基于规则匹配的一种入侵检测技术。Snort自1998年被发明以来,历经数年的迭代更新,Snort已成为一个具有多平台(Multi-Platform)、实时(Real-Time)流量分析、网络IP数据包(Pocket)记录等特性的强大的网络入侵检测/防御系统(Network Intrusion Detection/Prevention System),即NIDS/NIPS。
Snort首先需要使用者根据入侵行为的特征,按照一定的规范将这些特征编写成规则,最后通过检测网络数据与规则数据库中的规则是否匹配来判断入侵与否。在Snort入侵检测系统中,规则的匹配效率是影响Snort检测效率的关键。而规则匹配的核心技术是模式匹配算法。
请实现一个基于BF算法的模式匹配算法,用于网络入侵行为的检测。若检在网络日志中检测到任何一条检测规则中的ip地址,则输出“Intrusion.”,反之输出“No Intrusion.”
编程要求
输入
输入共n + m + 1行,第1行两个整数n, m(n, m ≤ 100) 2 ~ n + 1行每行为一条检测规则。规则的格式为:
protocol:网络协议 ip:ip地址 msg:"附加信息"
n + 2 ~ n + m + 1行共m行,为网络日志内容。
输出
输出一行,若检在网络日志中检测到任何一条检测规则中的ip地址,则输出“Intrusion.”,反之输出“No Intrusion.”。
#include<iostream>
#include<cstring>
#include<sstream>
using namespace std;
int BF(string s,string t)
{//简单模式匹配算法,S为主串(目标串),T为子串(模式串)。
//匹配成功返回主串中所含子串第一次出现的位置,否则返回-1。
/**************begin************/
int i = 0, 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 - j;
}
return -1;
/**************end************/
}
int main()
{
int n,m;
while (cin>>n>>m)//输入n条规则,并将其中的n个ip地址存放到ip数组,输入m条日志,并合并存放到log
{
cin.get();
string *rule=new string[n];
string log="";
for(int i=0;i<n;i++)
{
string t;
getline(cin,t);
stringstream sss(t);
string pot,ip,msg;
sss>>pot>>ip;
rule[i]=ip.substr(3, ip.length() - 3);
}
for(int i=0;i<m;i++)
{
string t;
getline(cin,t);
log += t;
}
for(int i=0;i<n;i++)
{
if(BF(log,rule[i])!=-1)
{
cout<<"Intrusion."<<endl;
return 0;
}
}
cout<<"No Intrusion."<<endl;
delete[] rule;
return 0;
}
}
第3关:基于KMP算法的网络入侵检测
任务描述
随着互联网的飞速发展,网络安全问题日益严重。入侵检测技术是一种积极主动防御的安全保障技术,而Snort是其中基于规则匹配的一种入侵检测技术。Snort自1998年被发明以来,历经数年的迭代更新,Snort已成为一个具有多平台(Multi-Platform)、实时(Real-Time)流量分析、网络IP数据包(Pocket)记录等特性的强大的网络入侵检测/防御系统(Network Intrusion Detection/Prevention System),即NIDS/NIPS。
Snort首先需要使用者根据入侵行为的特征,按照一定的规范将这些特征编写成规则,最后通过检测网络数据与规则数据库中的规则是否匹配来判断入侵与否。在Snort入侵检测系统中,规则的匹配效率是影响Snort检测效率的关键。而规则匹配的核心技术是模式匹配算法。
请实现一个基于KMP算法的模式匹配算法,用于网络入侵行为的检测。若检在网络日志中检测到任何一条检测规则中的ip地址,则输出“Intrusion.”,反之输出“No Intrusion.”
编程要求
输入
输入共n + m + 1行,第1行两个整数n, m(n, m ≤ 100) 2 ~ n + 1行每行为一条检测规则。规则的格式为:
protocol:网络协议 ip:ip地址 msg:"附加信息"
n + 2 ~ n + m + 1行共m行,为网络日志内容。
输出
输出一行,若检在网络日志中检测到任何一条检测规则中的ip地址,则输出“Intrusion.”,反之输出“No Intrusion.”。
#include<iostream>
#include<cstring>
#include<sstream>
using namespace std;
void GetNext(string pattern,int* next)
{//每个模式的下一个输出值 和 下一个输入数字组
int j=0, k=-1;
next[j]=k;
while (j<(int)pattern.length()-1){
if(k==-1||pattern[j]==pattern[k]){
j++;
k++;
next[j]=k;
}
else{
k=next[k];
}
}
}
int KMP(string target,string pattern,int* next)
{//KMP匹配算法,target为主串,pattern为子串。
//匹配成功返回主串中所含子串第一次出现的位置,否则返回-1。
//调用GetNext函数获取模式串的next数组
/**************begin************/
GetNext(pattern,next); //设置模式中的下一个数字组
int i=0, j=0;
while (i<(int)target.length()&&j<(int)pattern.length()){
if(j==-1||target[i]==pattern[j]) {
i++;
j++;
}
else { //模式方向正确
j=next[j];
}
}
if(j==pattern.length())
return i-j; //已成功匹配
return -1; //不匹配
/**************end************/
}
int main()
{
int n,m;
while (cin>>n>>m)
{
cin.get(); //将接收第一个返回,并阻止对netline()的输入
string *rule=new string[n]; //规则编号组存储ip地址
string log="";
for(int i=0;i<n;i++)
{
string t;
getline(cin,t);
stringstream sss(t); //根据规范将字段左侧分成一个字符
string pot,ip,msg;
sss>>pot>>ip;
rule[i]=ip.substr(3, ip.length() - 3);
}
for(int i=0;i<m;i++)
{
string t;
getline(cin,t);
log += t;
}
for(int i=0;i<n;i++)
{
int *next=new int[rule[i].length()];
if(KMP(log,rule[i],next)!=-1)
{
cout<<"Intrusion."<<endl;
return 0;
}
delete[] next; //删除组指针空间
}
cout<<"No Intrusion."<<endl;
delete[] rule;
return 0;
}
}
第4关:基于BM算法的网络入侵检测
任务描述
随着互联网的飞速发展,网络安全问题日益严重。入侵检测技术是一种积极主动防御的安全保障技术,而Snort是其中基于规则匹配的一种入侵检测技术。Snort自1998年被发明以来,历经数年的迭代更新,Snort已成为一个具有多平台(Multi-Platform)、实时(Real-Time)流量分析、网络IP数据包(Pocket)记录等特性的强大的网络入侵检测/防御系统(Network Intrusion Detection/Prevention System),即NIDS/NIPS。
Snort首先需要使用者根据入侵行为的特征,按照一定的规范将这些特征编写成规则,最后通过检测网络数据与规则数据库中的规则是否匹配来判断入侵与否。在Snort入侵检测系统中,规则的匹配效率是影响Snort检测效率的关键。而规则匹配的核心技术是模式匹配算法。
请实现一个基于BM算法的模式匹配算法,用于网络入侵行为的检测。若检在网络日志中检测到任何一条检测规则中的ip地址,则输出“Intrusion.”,反之输出“No Intrusion.”
编程要求
输入
输入共n + m + 1行,第1行两个整数n, m(n, m ≤ 100) 2 ~ n + 1行每行为一条检测规则。规则的格式为:
protocol:网络协议 ip:ip地址 msg:"附加信息"
n + 2 ~ n + m + 1行共m行,为网络日志内容。
输出
输出一行,若检在网络日志中检测到任何一条检测规则中的ip地址,则输出“Intrusion.”,反之输出“No Intrusion.”。
#include<iostream>
#include<cstring>
#include<sstream>
#include<algorithm>
using namespace std;
int* getBc(const string & pattern)
{//得到坏字符bc数组
int *bc=new int[256];
int len=pattern.length();
for (int i=0; i<256;++i)
bc[i]=-1;
for (int i=0; i<len;++i)
{
bc[pattern[i]]=i;
}
return bc;
}
int* suffixes(const string & pat)
{
const int len=pat.length();
int num;
int *suff=new int[len];
suff[len-1]=len;
for (int i=len-2;i>=0;--i)
{
for (num=0;num<=i&&pat[i-num]==pat[len-num-1];++num);
suff[i]=num;
}
return suff;
}
int* getGs(const string & pat)
{
const int len=pat.length();
const int lastIndex=len-1;
int *suffix=suffixes(pat);
int *gs=new int[len];
for(int i=0;i<len;++i)
gs[i]=len;
for(int i=lastIndex;i>= 0;--i)
{
if(suffix[i]==i+1)
{
for(int j=0;j<lastIndex-i;++j)
{
if(gs[j]==len)
gs[j]=lastIndex-i;
}
}
}
for(int i=0;i<lastIndex;++i)
{
gs[lastIndex-suffix[i]]=lastIndex-i;
}
delete[] suffix;
return gs;
}
int BM(const string & text, const string & pat)
{//在str.ch中匹配pattern.ch,匹配成功返回主串中所含子串第一次出现的位置,否则返回-1。
//分别求坏字符数组bc和好字符数组suffix、prefix,分别计算两个策略的移动位数,取大值作为最终移动位数
/**************begin************/
int *bc=getBc(pat);
int *gs=getGs(pat);
int patAt=0; //PatAt是指当前PatAt和代码文本对齐的位置
int cmp; //Cmp指当前偏移的位置
const size_t PATLASTID=pat.length()-1;
const size_t patLen=pat.length();
const size_t textLen=text.length();
while(patAt+patLen<=textLen)
{
//如果区域匹配成功,Lamp会将细条延伸到-1的位置
//patAt + cmp 指序列文本中当前位置的字符
for (cmp=PATLASTID;cmp>=0&&pat[cmp]==text[patAt+cmp];--cmp);
if (cmp==-1)
break;
else
{
patAt+=max(gs[cmp],cmp-bc[text[patAt+cmp]]);
}
}
delete[] bc;
delete[] gs;
return (patAt+patLen<=textLen)?patAt:-1;
}
int main()
{
int n,m;
while (cin>>n>>m)
{
cin.get(); //将接收第一个返回,并阻止对netline()的输入
string *rule=new string[n]; //规则编号组存储ip地址
string log=""; //log保存并接收
for(int i=0;i<n;i++)
{
string t;
getline(cin,t);
stringstream sss(t); //根据规范将字段左侧分成一个字符
string pot,ip,msg;
sss>>pot>>ip;
rule[i]=ip.substr(3, ip.length() - 3);
}
for(int i=0;i<m;i++)
{
string t;
getline(cin,t);
log += t;
}
for(int i=0;i<n;i++)
{
if(BM(log,rule[i])!=-1)
{
cout<<"Intrusion."<<endl;
return 0;
}
}
cout<<"No Intrusion."<<endl;
delete[] rule; //删除组指针空间
return 0;
}
}
第5关:统计字符出现的频度
任务描述
写一个算法统计在输入字符串中各个不同字符出现的频度,并将结果输出(字符串中的合法字符为A-Z之间的26个字母和0-9之间的10个数字)。
编程要求
输入
多组数据,每组数据有一行,为待统计字符频度的字符串。当字符串为“0”时,输入结束。
输出
对于每组数据输出n行,每一行为一个字符出现的频度(只输出存在的字符,格式为:字符:频度),字符排列顺序按照ASCII码从小到大的顺序。
#include<iostream>
#include<cstring>
using namespace std;
void Count(string c,int b[])
{//统计字符出现的频度
/**************begin************/
// 统计字符出现频度
for (int i = 0; i < c.length(); i++) {
if(c[i] >= '0' && c[i] <= '9') b[c[i] - '0']++;
else b[c[i] - 'A' + 10]++;
}
// 输出结果
for (int i = 0; i < 36; i++) {
if (b[i] > 0) { // 如果频度大于0,则输出
if(i >= 0 && i <= 9) cout << i << ":" << b[i] << endl; // 数字
else cout << (char)(i - 10 + 'A') << ":" << b[i] << endl; // 大写字母
}
}
/**************end************/
}
int main()
{
string c; //存储输入的字符串
while(cin>>c)
{
if(c=="0") break;
int *b=new int[36]{0}; //存储对应字符的个数
Count(c,b);
}
return 0;
}
第6关:递归实现字符串的逆序存储
任务描述
写一个递归算法来实现字符串的逆序存储,要求空间复杂度为O(1)。
编程要求
输入
多组数据,每组数据有一行,为要求逆序存储的字符串。当输入字符串为“0”时,输入结束。
输出
对于每组数据输出一行,为逆序存储后的字符串。
#include<iostream>
#include<cstring>
#define MAXSIZE 100
using namespace std;
void Reverse(char *a,int n)
{//递归实现字符串的逆序存储
/**************begin************/
if (n <= 0) return;
char temp = a[0];
a[0] = a[n-1];
a[n-1] = temp;
Reverse(a+1,n-2);
/**************end************/
}
int main()
{
char a[MAXSIZE];
while(cin>>a)
{
if(strcmp(a,"0")==0) break;
int i=0;
while(a[i]!='\0') i++; //统计字符数
Reverse(a,i);
cout<<a<<endl; //输出字符数组
}
return 0;
}
第7关:字符串的插入
任务描述
编写算法,实现下面函数的功能。函数void insert(char * s,char * t,int pos)将字符串t插入到字符串s中,插入位置为pos(插在第pos个字符前)。假设分配给字符串s的空间足够让字符串t插入。(说明:不得使用任何库函数)
编程要求
输入
多组数据,每组数据有三行,第一行为插入的位置pos,第二行为要被插入的字符串s,第三行为待插入的字符串t。当pos为“0”时输入结束。
输出
对于每组数据输出一行,为t插入s后的字符串。
#include<iostream>
#define MAXSIZE 100
using namespace std;
void Insert(char s[],char t[],int pos,int LenS,int LenT)
{//字符串的插入
int i;
for(i=LenS-1;i>=pos-1;i--) //字符串s的末尾先后移,记得补充接刚来
s[i+LenT]=s[i];
for(i=pos-1;i<pos-1+LenT;i++)
s[i]=t[i-pos+1]; //再将字符串b填入
LenS+=LenT; //更新字符串s的长度
s[LenS]='\0'; //字符串的末尾设置为结束符'\0'
cout<<s<<endl; //输出字符数组
}
int main()
{
int pos;
while(cin>>pos) //输入插入位置pos
{
if(pos<=0) break;
char s[MAXSIZE],t[MAXSIZE];
cin>>s>>t; //输入字符串s和t
int LenS=0,LenT=0;
while(s[LenS]!='\0') LenS++; //求字符串s的长度LenS
while(t[LenT]!='\0') LenT++; //求字符串t的长度LenT
if(pos>LenS) break; //插入位置非法
Insert(s,t,pos,LenS,LenT);
}
return 0;
}
第8关:查找子串第一次出现的位置
任务描述
在C语言中<string.h>头文件内,实现了strstr()函数,其函数原型为:
extern char *strstr(char *str1, const char *str2);
它的作用是:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
下面,请你设计算法简单实现函数strstr(),若存在返回子串首次出现的下标(从0开始),若不存在返回-1。
编程要求
输入
多组数据,每组数据有一行,分别为字符串str1、str2(用空格分开),当两串都为”0”时输入结束。
输出
对于每组数据分别输出一行,若子串存在返回子串首次出现的下标(从0开始),若不存在返回-1;
#include<iostream>
#include<string.h>
using namespace std;
int strstr(string s1,string s2)
{//查找子串第一次出现的位置
/**************begin************/
int len1 = s1.length();
int len2 = s2.length();
for (int i = 0; i <= len1 - len2; i++) {
int j;
for (j = 0; j < len2; j++) {
if (s1[i + j] != s2[j]) {
break;
}
}
if (j == len2) {
return i;
}
}
return -1;
/**************end************/
}
int main()
{
string s1, s2;
while(cin>>s1>>s2)
{
if (s1=="0"&&s2=="0") break;
int pos=strstr(s1,s2);
cout<<pos<<endl;
}
return 0;
}