目录
实验目的
- 掌握字符串的顺序存储表示方法。
- 掌握字符串模式匹配算法BF算法的实现。
实验内容(题目)
医学研究者最近发现了某些新病毒,通过对这些病毒的分析,得知它们的DNA序列都是环状的。现在研究者已收集了大量的病毒DNA和人的DNA数据,想快速检测出这些人是否感染了相应的病毒。为了方便研究,研究者将人的DNA和病毒DNA均表示成由一些字母组成的字符串序列,然后检测某种病毒DNA序列是否在患者的DNA序列中出现过,如果出现过,则此人感染了该病毒,否则没有感染。例如,假设病毒的DNA序列为baa,患者1的DNA序列为aaabbba,则感染;患者2的 DNA序列为babbba,则未感染。(注意:人的DNA序列是线性的,而病毒的DNA序列是环状的。)
实验环境
(1)软件系统
操作系统:Windows 10
编程环境:Embarcadero Dev-C++ 6.3,包含GNU GCC编译器和GDB调试器
(2)硬件系统
CPU:Intel(R) Core(TM) i7-10875H CPU @ 2.30GHz
内存:16GB
硬盘:500GB SSD
程序代码
#include <stdio.h> // 标准输入输出头文件
#include <string.h> // 字符串操作头文件
#define YES 1
#define NO 0
// 全局变量定义
char V[20]; // 存储病毒DNA序列的数组
char D[20]; // 存储人类DNA序列的数组
// 使用暴力匹配算法进行字符串匹配的函数
int BF(char *D, char *V) {
int i = 0, j = 0; // i是主字符串的索引,j是模式字符串的索引
// 遍历主字符串D和模式字符串V
while (i < strlen(D) && j < strlen(V)) {
if (D[i] == V[j]) { // 如果当前字符匹配
i++;
j++;
} else { // 如果字符不匹配
i = i - j + 1; // 主字符串回溯
j = 0; // 模式字符串重置
}
}
// 如果模式字符串V完全匹配,返回YES
if (j >= strlen(V)) return YES;
// 否则,返回NO
else return NO;
}
// 处理环状DNA序列的函数,利用循环展开来匹配
int BFjudge(char *D, char *V) {
int flag = NO; // 默认匹配标志为NO
int i, j, m;
char temp[20]; // 临时存储展开的环状DNA序列
m = strlen(V); // 获取模式字符串V的长度
// 将模式字符串V复制到其末尾,形成循环展开的字符串
for (i = m, j = 0; j < m; j++) {
V[i++] = V[j];
}
V[2 * m] = '\0'; // 确保字符串以null字符终止
// 遍历循环展开的字符串
for (i = 0; i < m; i++) {
// 构建当前子串temp
for (j = 0; j < m; j++) {
temp[j] = V[i + j];
}
temp[m] = '\0'; // 确保子串以null字符终止
// 使用BF算法进行匹配
flag = BF(D, temp);
if (flag) break; // 如果匹配成功,退出循环
else if (i >= m) return NO; // 如果超过了环状DNA序列的长度,返回NO
}
return flag ? YES : NO; // 返回匹配结果
}
// 输入和输出处理函数
int PRINThand() {
int i = 0, k = 0;
int s[20]; // 存储匹配结果的数组
printf("请输入病毒DNA及人的DNA(输入0 0结束):\n");
while (1) {
scanf("%19s", V); // 读取病毒DNA序列
scanf("%19s", D); // 读取人类DNA序列
if (V[0] == '0' && D[0] == '0') break; // 检测到输入为'0 0'则结束循环
// 调用BFjudge函数并存储结果到数组s
s[k] = BFjudge(D, V);
k++; // 结果数组索引递增
}
// 根据结果数组s输出所有匹配结果
printf("病毒感染检测输出结果:\n");
for (i = 0; i < k; i++) {
printf("%s\n", s[i] == YES ? "YES" : "NO");
}
return 0; // 函数返回0表示成功执行
}
// 主函数
int main() {
PRINThand(); // 调用输入和输出处理函数
return 0; // 程序结束,返回0
}
实验分析
从以下方面分析:
1、正确性(输入的测试数据和测试结果,附测算截屏)
这个程序的目的是为了检测人类DNA序列中是否存在特定的病毒DNA序列。程序使用暴力匹配(Brute Force, BF)算法来找出模式字符串(这里是病毒DNA)是否在主字符串(人类DNA)中。
测算截屏:
2、可读性(主要在源代码中体现)
- 代码结构清晰,具有明确的模块划分。
- 函数命名合理,符合其功能。
- 注释充分,有助于理解代码逻辑。
3、健壮性(容错性,主要在源代码中体现,在此简要说明)
- 优点:
- 检查了结束输入的条件(当用户输入"0 0"时)。
- 使用了%19s来限制scanf读取的字符数,避免了缓冲区溢出。
- 缺点:
- 程序没有处理异常输入或非预期的情况,如非数字字符输入、过长的DNA序列或空字符串。
- 因为使用了全局变量,程序的健壮性受限,容易被意外的修改影响。
4、时间和空间复杂度(针对核心算法函数分析)
- 时间复杂度:暴力匹配算法的时间复杂度是O(n*m),其中n是主字符串的长度,m是模式字符串的长度。在最坏的情况下,每个字符都要比较一次,所以是n和m的乘积。在BFjudge中,我们对于长度为m的模式字符串,最多做了m次匹配尝试,每次都是O(m)的时间复杂度,因此整体是O(m^2)。
- 空间复杂度:程序使用固定大小的数组(20个字符),所以空间复杂度为O(1),也就是常数空间。temp数组在BFjudge函数中用于存储循环展开后的字符串,长度也是固定的。
实验到此结束!
点赞关注,获取更多干货~