第四章小结

  第四章主要学习了串和数组,串的模式匹配算法(BF算法实现简单一些,但是时间复杂度为O(m*n),效率低;KMP算法比较复杂,时间复杂度为O(m+n),效率高)和数组的顺序存储和特殊矩阵的压缩存储(对称矩阵、三角矩阵和稀疏矩阵)。通过作业中的两道编程题,基本掌握了BF算法、KMP算法、三元组实现稀疏矩阵。最后一道编程题是AI核心代码,这道题目让我对字符串的处理操作有了更深的理解和体会。以下是我在解决这道题目时的心得体会:

  首先,在老师提供的思路的基础上,分享一下我的思路

1.确定数据结构,这道题目自然是字符串,可以选择字符数组或string实现

2.写出main()函数,确定调用函数

 1 int main() {
 2     int n;        //行数n,s存放输入的语句
 3     string s;
 4     cin >> n;
 5     getchar();         //吸收回车符
 6     for (int i = 1; i <= n; i++) {
 7         getline(cin, s);
 8         cout << s << endl;        //先输出原话,再输出处理后的AI回答
 9         cout << "AI: ";
10         go(s);    //处理并输出回答
11     }
12     return 0;
13 }

这里有用到getchar()函数,刚开始见到的时候还不太清楚这个函数的用法,所以查了资料(https://blog.csdn.net/jiangxinnju/article/details/20492453 里面分享了

cin、cin.getline、cin.get、getline、gets、getchar的用法实例 )

 

3.写调用函数go(s)

 1 void go(string s) 
 2 { //根据s处理并输出回答
 3     char t[3002];    //t为处理后的字符串
 4     int i, j = 0;        //i定位到s的第一个非空,j表示t串的字符
 5     for (i = 0; s[i] == ' '; i++)  ; //跳过开头多余的空格,定位到s的第一个非空字符
 6     putin(s, t, i, j);    //对s进行预处理
 7     t[j] = '\0';    //为t串末尾增加结尾符
 8     j = 0;        //为方便遍历将j置零
 9     print(t,j);        //遍历t并继续进行修改操作后输出
10     cout << endl;
11 }    

 

这里我选择了多写两个调用函数,这样思路看起来也可以更清晰一点。一开始的时候我是直接全部代码都打在go函数里,除了判断独立的函数外,没有再写调用函数,所以go函数很长,代码很多,导致对两次退出循环后字符串t的操作很混乱,而且还很难发现其中的错误。

处理空格有三种情况,开头多余的空格、两个单词之间多余的空格、标点符号前多余的空格

这里首先对开头空格进行处理,如果匹配到多余的空格,则改变字符串s的下标i,跳过空格,找到第一个非空字符。

 

4.按照要求对字符串进处理

第一步,对字符串s的预处理,并把处理好的字符复制到字符串t中,这就是putin函数,函数执行完字符串开头没有多余空格,只剩单个空格,?改成!,大写字母变小写字母(除了I)

 1 //把s输入给t
 2 void putIn(string s, char t[],int &i, int &j) {
 3     while (s[i] != '\0') {    
 4         if (s[i] == ' '&&s[i - 1] == ' ') {        //跳过多余的空格
 5             i++;
 6             continue;
 7         }
 8         if (s[i] == '?') {        //将输入的问号变为感叹号
 9             t[j] = '!';
10             i++;
11             j++;
12             continue;
13         }
14 
15         if (s[i] != 'I') {        //将除了I的大写变小写
16             t[j] = tolower(s[i]);
17             i++;
18             j++;
19         }
20         else {
21             t[j] = s[i];    //将s串的非空或者单个空格给到t串,之后分别+1进行下一轮输入
22             j++;
23             i++;
24         }
25     }
26 }

putin函数执行完后一定要给字符串t再复杂终止符'\0',即 t[ j ] = '\0';这样字符串t才是合理的。

 

第二步,对字符串t进行修改,将j置为0,因为要遍历t

 1 void print(char t[], int &j) {
 2     while (t[j] != '\0') {
 3         //独立的I,意味着左右均是分隔符
 4         if (t[j] == 'I' && (j == 0 || isIndependent(t[j - 1])) && isIndependent(t[j + 1])) {
 5             cout << "you";
 6             j++;
 7             continue;
 8         }
 9         //独立的me
10         if (t[j] == 'm'&&t[j + 1] == 'e' && (j == 0 || isIndependent(t[j - 1])) && isIndependent(t[j + 2])) {
11             cout << "you";
12             j += 2;
13             continue;
14         }
15 
16         //独立的can you
17         if (isCanyou(t, j)) {
18             cout << "I can";
19             j += 7;
20             continue;
21         }
22 
23         //如果是标点前的空格就不输出
24         if (t[j] == ' '&&isIndependent(t[j + 1])) {
25             j++;
26             continue;
27         }
28         cout << t[j];
29         j++;
30     }
31 }

注意:当需要将独立的I,me换成you时,特别注意me的情况,j不能只加1,需要加2,跳过me到下一个字符;将can you换成I can时也是如此,j要加7,才能跳到下一个待比较的字符。而且,这道题目还要求判断独立,如果只判断该字符前后是否为空格或标点符号是不够,还有可能会溢出。这时,老师又教给我们一个很重要的技巧了,就是判断下标是否为0,这就是判断该字符是否在开头,在开头的情况下标是不能再减的了,所以就有了

(j == 0 || isIndependent(t[j - 1])) && isIndependent(t[j + 1])

真的是太优秀了

这里还处理了标点符号前的空格,我个人觉得这里调用的函数很有意思,判断标点符号就是判断不是字母不是数字不是空格。

最后,print函数后面一定要加上cout<<endl; 在每输出字符串t后能换行,这样才能格式正确。

全部代码为

  1 #include <iostream>
  2 #include <string>
  3 #include <cstring>
  4 using namespace std;
  5 
  6 void go(string s);
  7 bool isIndependent(char ch);
  8 bool isCanyou(char ch[], int n);
  9 void putin(string s, char t[], int &i, int &j);
 10 void print(char t[], int &j);
 11 
 12 int main() {
 13     int n;        //行数n,s存放输入的语句
 14     string s;
 15     cin >> n;
 16     getchar();
 17     for (int i = 1; i <= n; i++) {
 18         getline(cin, s);
 19         cout << s << endl;        //先输出原话,再输出处理后的AI回答
 20         cout << "AI: ";
 21         go(s);    //处理并输出回答
 22     }
 23     return 0;
 24 }
 25 
 26 //根据s处理并输出回答
 27 void go(string s) {
 28     char t[3002];    //t为处理后的字符串
 29     int i, j = 0;        //i定位到s的第一个非空,j表示t串的字符
 30     for (i = 0; s[i] == ' '; i++) {
 31         //仅仅用于定位.因为字符串有个结尾符‘\0’,所以及时字符串全空,到最后的结尾符也会停止循环
 32     }
 33     putin(s, t, i, j);    //把s输入给t
 34     t[j] = '\0';    //为t串末尾增加结尾符
 35     j = 0;        //为方便遍历将j置零
 36     print(t,j);        //遍历t并一顿操作后输出,主要把I,me变成you; can you 换成 I can
 37     cout << endl;
 38 }
 39 
 40 //判断字符是否为分隔符
 41 bool isIndependent(char ch) {
 42     bool result = true;
 43     ch = tolower(ch);
 44     if ((ch >= '0' && ch <= '9') || (ch >= 'a'&&ch <= 'z')) {
 45         result = false;
 46     }
 47     return result;
 48 }
 49 
 50 //判断是否为独立的can you
 51 bool isCanyou(char ch[],int n) {
 52     bool result = false;
 53     if (ch[n] == 'c'&&ch[n + 1] == 'a'&&ch[n + 2] == 'n'&&ch[n + 3]==' ' && ch[n + 4] == 'y'&&ch[n + 5] == 'o'&&ch[n + 6] == 'u') {
 54         if ((n == 0 || isIndependent(ch[n - 1])) && isIndependent(ch[n + 7])) {
 55             result = true;
 56         }
 57     }
 58     return result;
 59 }
 60 
 61 //把s输入给t
 62 void putIn(string s, char t[],int &i, int &j) {
 63     while (s[i] != '\0') {    
 64         if (s[i] == ' '&&s[i - 1] == ' ') {        //跳过多余的空格
 65             i++;
 66             continue;
 67         }
 68         if (s[i] == '?') {        //将输入的问号变为感叹号
 69             t[j] = '!';
 70             i++;
 71             j++;
 72             continue;
 73         }
 74 
 75         if (s[i] != 'I') {        //将除了I的大写变小写
 76             t[j] = tolower(s[i]);
 77             i++;
 78             j++;
 79         }
 80         else {
 81             t[j] = s[i];    //将s串的非空或者单个空格给到t串,之后分别+1进行下一轮输入
 82             j++;
 83             i++;
 84         }
 85     }
 86 }
 87 
 88 //遍历t并一顿操作后输出,主要把I,me变成you; can you 换成 I can
 89 void print(char t[], int &j) {
 90     while (t[j] != '\0') {
 91         //独立的I,意味着左右均是分隔符
 92         if (t[j] == 'I' && (j == 0 || isIndependent(t[j - 1])) && isIndependent(t[j + 1])) {
 93             cout << "you";
 94             j++;
 95             continue;
 96         }
 97         //独立的me
 98         if (t[j] == 'm'&&t[j + 1] == 'e' && (j == 0 || isIndependent(t[j - 1])) && isIndependent(t[j + 2])) {
 99             cout << "you";
100             j += 2;
101             continue;
102         }
103 
104         //独立的can you
105         if (isCanyou(t, j)) {
106             cout << "I can";
107             j += 7;
108             continue;
109         }
110 
111         //如果是标点前的空格就不输出
112         if (t[j] == ' '&&isIndependent(t[j + 1])) {
113             j++;
114             continue;
115         }
116         cout << t[j];
117         j++;
118     }
119 }
View Code
 1 #include<iostream>
 2 #include<string>
 3 using namespace std;
 4 
 5 bool isAlone(char c)
 6 { //判断字符是否为空格或标点符号 
 7     c=tolower(c);
 8     if((c>='0'&&c<='9') || (c>='a'&&c<='z')) return false;  
 9     else return true; 
10 }
11 
12 bool isPunctuation(char c)
13 { //判断字符是否为标点符号
14     c=tolower(c);
15     if((c>='0'&&c<='9') || (c>='a'&&c<='z') || c==' ') return false;
16     else return true; 
17 }
18 
19 void go(string s)
20 {
21     string t;
22     int i,j;
23     for(i=0;s[i]!='\0' && s[i]==' ';i++) ; //跳过开头多余的空格,定位第一个非空字符 
24     j=0;
25     while(s[i]!='\0')
26     {
27         if(s[i]==' '&&s[i-1]==' ') 
28         { //跳过多余的空格
29             i++;
30             continue;
31         }
32         if(s[i]=='?') 
33         { //将输入的问号变为感叹号
34             t[j]='!';
35             j++;
36             i++;
37             continue;
38         }
39         if(s[i]!='I') 
40         { //将除了I的大写字母变小写
41             t[j++]=tolower(s[i++]);
42             continue;
43         }
44         else  
45         { //将s中的非空字符或单个空格复制到t
46             t[j++]=s[i++];
47         }
48         
49     }
50     t[j]='\0'; //t串最后加上结尾符 
51     j=0; //为方便遍历将j置零
52     while(t[j]!='\0')
53     { 
54         if(t[j]=='I' && (j==0 || (isAlone(t[j-1]) && isAlone(t[j+1]))))
55         { //独立的I,换成you 
56             cout<<"you";
57             j++;
58             continue;
59         }
60         if(t[j]=='m' && t[j+1]=='e' && (j==0 || (isAlone(t[j-1]) && isAlone(t[j+2]))))
61         { //独立的me,换成you 
62             cout<<"you";
63             j+=2;
64             continue;
65         }
66         if(t[j]==' ' && isPunctuation(t[j+1]))
67         { //删除标点符号前多余的空格 
68             j++;
69         }
70         if(t[j]=='c'&&t[j+1]=='a'&&t[j+2]=='n'&&t[j+3]==' '&&t[j+4]=='y'&&t[j+5]=='o'&&t[j+6]=='u')
71         { //独立的can you,换成I can 
72             if(j==0 || (isAlone(t[j-1]) && isAlone(t[j+7])))
73             {
74                 cout<<"I can";
75                 j=j+7;
76                 continue;
77             }
78         }
79         cout<<t[j++];
80     }
81     cout<<endl;
82 } 
83  
84 int main()
85 {
86     string s;
87     int n;
88     cin>>n;
89     getchar();
90     for(int i=0;i<n;i++)
91     {
92         getline(cin,s);
93         cout<<s<<endl;
94         cout<<"AI: ";
95         go(s);
96     }
97     return 0;
98 } 
View Code

(第一个是增加了调用函数,并参考了资料

   第二个是没有增加的,是自己写)

做完这道题,最大的体会是真的要提高自己的编程能力了,基础知识真的很重要,多编程,注意细节,理清思路,注意语法

刚做这道题的时候,字符串t定义为string类型,语句为cout<< t <<endl; 时一直无法输出,当看到老师的博客才知道要写成cout << t.data() << endl; 

因为之前一直无法输出,所以进度停滞不前,所以基础的语法还是很重要的。还有很多编程的技巧需要学习,只有这样才能更好更快的编程。字符串处理的时候还需要注意溢出的问题,只有考虑边界问题,考虑多种情况,才能准确写出循环条件,才能把题目做对。

 

上次的目标是学完多种数据结构也能不混乱,完成情况还可以,学了四章后也不是很混乱,虽然不算很熟练,但是会继续努力的。这次的目标是多做编程题,感受一下作业之外的编程题。

 

posted @ 2019-04-13 20:58  Jimin~  阅读(188)  评论(1编辑  收藏  举报