实验报告:统计字符串中子字符串出现的次数

实验报告

源程序:

 

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int cntstring(char *source,char *substr);
 5 void main()
 6 {
 7     char str1[50],str2[20];       //str1 为主字符串,str2 为子字符串
 8     cout<<"input source string:";
 9     cin.getline(str1,50);
10     cout<<"input sub string:";
11     cin.getline(str2,20);
12     cout<<"Occurs:"<<cntstring(str1,str2)<<endl;
13 }
14 int cntstring(char *source,char *substr)
15 {
16     int sum=0;                   //统计子字符串数量初值为0
17     char *p1=source,*p2=substr;
18     while(*p1 !='\0')               //主字符串没有结束
19     {
20         if(*p1 == *p2)               //第一个字符相同
21         {
22             while(*p1 == *p2 && *p2 !='\0')   //循环比较后续字符
23             {
24                 p1++;p2++;            //子字符串没有结束,两字符串同事后移一个字符
25             }
26             
27         }
28         else
29         {
30             p1++;                 //主字符串后移,重新比较
31         }
32             
33         if(*p2 == '\0')
34             sum++;
35         p2=substr;                //字字符串出现一次,指针重新指向子字符串
36     }
37     
38     return sum;                  //返回统计结果
39 }

 

提示:主函数使用cin.getline读入主字符串和子字符串。函数cntstring对于主字符串和子字符串从第一个字符开始逐一向后比较。第二个while循环表示当主字符串的当前字符与子字符串的第一个字符相同时,循环比较后续的字符是否相同,若循环完毕且指向子字符串的指针移到子字符串的末尾,则认为完成一次成功匹配,否则主字符串后移一个字符,再重新比较。

————————————————————————————

输入: input source string:This is a C++ program.↙

      input sub string:is↙

输出结果:Occurs:2

————————————————————————————

二、实验要求

对该题目进行下述修改,并对修改后的程序运行结果进行分析

错误分析:

重新运行程序,输入如下数据:

input source string:aaabc↙

input sub string:aabc↙

则输出结果为:Occurs:0

(1)对于错误分析中提出的问题,采用单步调试技术分析出错的原因。修改程序使其正确。

(2)修改程序,使其能够同时输出子字符串在主字符串中出现的每一个位置(该位置默认为子字符串中第一个字符在主字符串中出现的位置)。

 

三、实验结果以及分析(红色并下划线的是增加的代码

(1)将上述源程序代码更改为如下所示代码:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int cntstring(char *source, char *substr);
 5 void main()
 6 {
 7     char str1[50], str2[20];
 8     cout << "input source string:";
 9     cin.getline(str1, 50);
10     cout << "input sub string:";
11     cin.getline(str2, 20);
12     cout << "Occurs:" << cntstring(str1, str2) << endl;
13 }
14 int cntstring(char *source, char *substr)
15 {
16     int sum = 0;
17     char *p1 = source, *p2 = substr;
18     while (*p1 != '\0')
19     {
20         if (*p1 == *p2)
21         {
22             while (*p1 == *p2 && *p2 != '\0')
23             {
24                 p1++; p2++;
25             }
26 
27         }
28         else
29         {
30             p1++;
31         }
32 
33         if (*p2 == '\0') 
34         {
35             sum++;
36         37         }
38             
39         p2 = substr;
40         p1 = ++source;//←为添加的代码;
41     }
42 
43     return sum;
44 }

分析

采用单步调试技术进行分析,拿错误分析中输入的数据 aaabc 和 aabc 来举例子

首先看实验流程,main函数将输入的两个字符串str1,str2作为实参,传递给*source,*substr的指针变量。

在用户函数cntstring中,声明字符型指针变量p1,p2,并将source(即str1的首地址)赋给p1,将substr(即str2的首地址)赋给p2。

看函数执行部分,当*p1(str1的首地址)不等于结束符'\0'时,进入while循环,如果*p1与*p2(str2的首地址)相等,则进入if语句,当指针p2还未指向结束符'\0'时,指针p1与p2同时向后移动一个字符,指向下一个字符。如str1为"aaabc",str2为"aabc",开始时,p1指向str1中的a,p2指向str2中的a,字符相同,两个指针同时向后移动。当指向的字符不相同时,指针p1指向下一个字符,当p2还未指向结束符'\0'时,将p2指针初始化,指向str2的第一个字符,同时也将p1指针初始化,指向str1的第二个字符(注意++source为指针运算,指针可以自增)。若p2指向了结束符,意味着已经找到了子字符串,故sum++(若没有找到子字符串p2是不可能指向结束符的)。当p1还未指向str1的结束符时,再次循环,从头上判断 字符是否相等开始(注意此时p1已经指向str1的第二个字符)。相同步骤以此类推

对于aaabc和aabc,前两个字符是相同的,当同时指向各自的第三个字符时,字符不相同,执行else语句中的p1++,p1指向了字符b,但后面有p1指针的初始化(p1=++source),故该轮循环结束后,p1指向str1的第二个字符a,p2也初始化,指向str2的第一个字符a,又开始一轮循环,则后面字符都相同,直到p2指向结束符,p1也指向结束符,执行了sum++语句,即sum=0+1,sum=1;返回值给主函数输出 Occurs:1;

(建议自己拿一只笔,用箭头模仿指针走向,按照程序运行的步骤来分析)

(2)将第一题中的代码改为如下代码:

 

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int cntstring(char *source, char *substr);
 5 void main()
 6 {
 7     char str1[50], str2[20];
 8     cout << "input source string:";
 9     cin.getline(str1, 50);
10     cout << "input sub string:";
11     cin.getline(str2, 20);
12     cout << "Occurs:" << cntstring(str1, str2) << endl;
13 }
14 int cntstring(char *source, char *substr)
15 {
16     int sum = 0;
17     char *p1 = source, *p2 = substr,*p0 = source;
18     while (*p1 != '\0')
19     {
20         if (*p1 == *p2)
21         {
22             while (*p1 == *p2 && *p2 != '\0')
23             {
24                 p1++; p2++;
25             }
26 
27         }
28         else
29         {
30             p1++;
31         }
32 
33         if (*p2 == '\0') 
34         {
35             sum++;
36             cout << "postion=" << source-p0+1<< endl;
37         }
38             
39         p2 = substr;
40         p1 = ++source;
41     }
42 
43     return sum;
44 }

 

分析

将str1的首地址赋给一个不变化的指针*p0(因为在后面运行中source会自增,设一个指针变量将其首地址保存下来)。

对于位置:由指针的运算可知,指针相减得出来的是整数且整数的值为两个指针指向的地址之间的数据单元的数量,举个栗子:

当p1指向aaabc中的b的时候,则p1-source(首地址,为第一个a)=3;(3个数据单元)

由于当str1,str2中的字符开始相等时,会进入while循环,就不会每指向下一个字符的时候进行初始化(p2 = substr;p1 = ++source;

此时source保存着子字符串第一个字符在主字符串中的位置,比如主字符串为aaabc,子字符串aabc;source的值为主字符串中第二个a的地址

故source-p0等于1,再加1就是第二个a的位置,即子字符串在主字符串的中的位置。(仔细理解一下,我写的可能不够形象)

 

算法思想:指针的运算以及指针的移动指向。(具体看题(1)中的解释)

 

以上就是一道例题以及扩展题目的展示。若有更好的方法希望指出,谢谢!

 

posted @ 2016-12-06 19:44  南溪  阅读(5235)  评论(0编辑  收藏  举报