目录
1 需求分析
1.1 任务
某些病毒DNA为环形序列,现研究人员已收集了大量病毒DNA序列和人的DNA序列数据,并将DNA表示成由一些字母组成的字符串序列,然后检测某种病毒DNA序列是否在人DNA中出现,若出现,则该患者已感染该病毒,否则没有感染。即该程序将用于检测子串(病毒序列)是否在主串(患者DNA)中出现。特别注意,人的DNA序列为线性,但病毒序列为环形。
1.2 输入、输出形式
输入形式:多组数据,每组数据有1行,为序列A和B,A对应病毒的DNA序列,B对应人的DNA序列。当A、B均为“0”时输入结束,退出程序执行。
输出形式:对于每组数据输出1行,若患者感染了病毒输出“YES”,否则输出“NO”。特殊的,当程序退出时提示程序退出。
具体示例如下图
1.3 程序功能
(1)基于顺序存储结构的字符串初始化:为定义的字符串分配字符存储空间,并初始化其内部其他数值。
(2)模式匹配:对两字符串进行比较,判断主串中是否包含子串。若包含,则返回‘Y’,否则返回‘N’。、
(3)循环改变子串:子串为循环系列,因此当一轮比较完成之后需要对子串进行修改,修改方式则是将字符串最后一个字符放到字符串第一个位置。
2 概要设计
2.1 抽象数据类型定义
typedef struct S{
char *str; //存放字符串
int length; //当前字符数量
int size; //当前存储空间大小
/* str采用顺序结构存储字符串,length保存字符串长度,size保存当前str空间的总大小*/
}String;
2.2 主程序流程图
3 详细设计
3.1 存储空间初始化
Status InitString(String *S){ //初始化字符串存储空间
(*S).str=(char*)malloc(Init_String*sizeof(char));
if(!(*S).str) exit(OVERFLOW);
(*S).length=0;
(*S).size=Init_String;
return OK;
}
3.2 模式匹配
char Modlecmp(String son,String main){ //模式匹配
int flag,i,j;
flag=0;
for(i=0,j=0;i<main.length+1; ){
if(j==son.length){
//当子串指针等于子串长度时,即匹配成功;则flag置1,退出循环;
flag=1;break;
}
if(son.str[j]==main.str[i]){
//当子串与主串指针当前所指位置字符一致,两个指针后移,并跳过后续代码
i++;j++;continue;
}
if(son.str[j]!=main.str[i]){
//当子串与主串指针当前所指位置字符不一致,子串指针置0
j=0;
if(son.str[j]!=main.str[i]) i++; //当子串与主串指针当前所指位置字符再次不一致,主串指针后移
}
}
if(flag==1) return 'Y';
return 'N';
}
3.3 循环修改子串
Status Change(String *s){
int i;
if(s->length>=s->size){ //判断存储空间是否足够
s->str=(char *)realloc(s->str,(Init_String+StringIncrease)*sizeof(char));
if(!s->str) exit(OVERFLOW);
s->size+=StringIncrease;
}
for(i=s->length-1;i>=0;i--){ //从后依次将字符后移一位
s->str[i+1]=s->str[i];
}
s->str[0]=s->str[s->length]; //将最后一位字符放置到第一位
s->str[s->length]='\0'; //原先最后一位字符位置置'\0'
return OK;
}
4 调试分析
4.1 调试过程存在的问题
(1)问题1:调用模式匹配无法正常返回,即“result=Modlecmp(str_son,str_main);”语句无法正常返回并赋值。
分析:模式匹配模块代码设计错误,推测使其中的循环进入死循环导致无法终止。
解决:重写模式匹配模块代码。
(2)问题2:比较不完全。在调用模式匹配函数的时候,当输入为类似于“abahjakdjaba”(即子串出现在主串末尾)时,函数返回值总是为‘N’;
分析:子串与主串没有完全匹配完成就退出循环:
解决:增加循环次数,将“i<main.length”改为“i<main.length+1”,使得子串与主串充分匹配。
4.2 时空复杂度
4.3 经验体会
(1)gets和scanf的异同:gets和scanf都可以用于字符串的输入;但gets为字符串输入的一种专用函数,可以读入一切字符,并以“回车”符为输入终止符。因此gets函数可以读入空格将一行形成一个字符串。但是scanf为标准格式化输入函数,以空格、tab、回车等为输入终止符,不会将输入的一行组成一个字符串,而是会依据空格切分。
(2)算法思想与代码转换:一个算法思想设计完成之后,在转换成代码的时候要多考虑细节问题,不能与思想一样过于宽泛。如果代码过于宽泛容易造成运行结果错误或者无法运行的情况。可以首先在草稿纸上写出简易代码并手动执行一次,再用软件编写代码,降低出错的可能性。
(3)单步分析:当代码执行出错时,可以使用单步跟踪的分析方法来对代码进行分析。可以使用“printf”将代码关键变量的值输出到屏幕,来分析关键变量值是否正确,以此来判断代码执行情况。对于函数调用方面,可以在函数中加入“printf”来作为函数是否调用的标记。