第四章小结
第四章主要学习了串和数组,串的模式匹配算法(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 }
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 }
(第一个是增加了调用函数,并参考了资料
第二个是没有增加的,是自己写)
做完这道题,最大的体会是真的要提高自己的编程能力了,基础知识真的很重要,多编程,注意细节,理清思路,注意语法。
刚做这道题的时候,字符串t定义为string类型,语句为cout<< t <<endl; 时一直无法输出,当看到老师的博客才知道要写成cout << t.data() << endl;
因为之前一直无法输出,所以进度停滞不前,所以基础的语法还是很重要的。还有很多编程的技巧需要学习,只有这样才能更好更快的编程。字符串处理的时候还需要注意溢出的问题,只有考虑边界问题,考虑多种情况,才能准确写出循环条件,才能把题目做对。
上次的目标是学完多种数据结构也能不混乱,完成情况还可以,学了四章后也不是很混乱,虽然不算很熟练,但是会继续努力的。这次的目标是多做编程题,感受一下作业之外的编程题。