第四章小结
第四章主要學習了串和數組,串的模式匹配算法(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;?
因為之前一直無法輸出,所以進度停滯不前,所以基礎的語法還是很重要的。還有很多編程的技巧需要學習,只有這樣才能更好更快的編程。字符串處理的時候還需要注意溢出的問題,只有考慮邊界問題,考慮多種情況,才能準確寫出循環條件,才能把題目做對。
?
上次的目標是學完多種數據結構也能不混亂,完成情況還可以,學了四章后也不是很混亂,雖然不算很熟練,但是會繼續努力的。這次的目標是多做編程題,感受一下作業之外的編程題。
?
轉載于:https://www.cnblogs.com/jiminxi/p/10702872.html
總結
- 上一篇: 第七周编程总结
- 下一篇: Java中的队列同步器AQS