第四章学习小结
KMP真是神奇的算法,原理是懂了,可敲代码的时候还是显得稀里糊涂的。
那就说说AI算法题吧
本题要求你实现一个简易版的 AI 英文问答程序,规则是:
- 无论用户说什么,首先把对方说的话在一行中原样打印出来;
- 消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
- 把原文中所有大写英文字母变成小写,除了 I;
- 把原文中所有独立的 I 和 me 换成 you;
- 把原文中所有的问号 ? 换成惊叹号 !;
- 把原文中所有独立的 can you 换成 I can —— 这里“独立”是指被空格或标点符号分隔开的单词;
- 在一行中输出替换后的句子作为 AI 的回答。
输入格式:
输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。
输出格式:
按题面要求输出,每个 AI 的回答前要加上 AI: 和一个空格。
第一反应:好长,要考虑的好多,好难。。。。。。
不过实践课上老师化整为零的讲了讲发现好像还行,自己能试试(试试就逝世)
首先对整体做一个规划,在主函数里列出大致思路
int main() { string s; int n, i; cin >> n; getchar(); //吸收回车符 for (i = 1; i <= n; ++i) { getline(cin, s); cout << s<<endl; cout << "AI: "; go(s);//AI根据s输出对话 } return 0; }
然后就是go函数了,这方面要考虑的偏多(尤其是各种越界情况)
首先考虑前面全是空格的情况,那么我们先定位到开头非空的地方
char t[3001]; //全是I时达最长长度,加三倍 int i = 0, j; //i,j分别为s,t的下标 //i定位到s的第一个非空字符 for (; s[i] == ' '; ++i); //循环体为空
之后就要考虑其他空格的情况,消去多余的空格
while (s[i] != '\0') { if (s[i] == ' ' && s[i - 1] == ' ') { ++i; //如果漏了这句,有连续空格时会死循环 continue; }
之后是单个字符转化的问题,这方面比较简单且没什么坑
if (s[i] == '?') { t[j++] = '!'; ++i; continue; } if (s[i] != 'I') { t[j++] = tolower(s[i++]); continue; } t[j++] = s[i++]; //s[i]='I'的情况 若为me则改成you
至于多个字符整体转化的问题,我们对输出数列进行操作,由于牵扯到j-1这样的东西,需考虑越界情况
while (t[j] != '\0') { if (t[j] == 'I' && (j == 0 || isSeparator(t[j - 1])) && isSeparator(t[j + 1])) { cout << "you"; ++j; continue; } if (t[j] == 'm' && t[j + 1] == 'e' && (j == 0 || isSeparator(t[j - 1])) && isSeparator(t[j + 2])) { cout << "you"; j += 2; continue; } if ((j == 0 || isSeparator(t[j - 1])) && 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'&&isSeparator(t[j + 7])) { cout << "I can"; j += 7; continue; } if (t[j] == ' '&&iswhite(t[j + 1])) { j++; continue; } cout << t[j++]; } cout << endl; }
然后好像就差不多了?似乎这题也就这样了吧。。。不过自己写的时候可能思路还不会这么清晰,而且一旦出现bug就可能再也出不去了。
整体代码贴一下
#include<iostream> #include<string> using namespace std; bool iswhite(char ch) { ch = tolower(ch); if ((ch >= '0' && ch <= '9') || (ch >= 'a'&&ch <= 'z')) { return false; } return true; } bool isSeparator(char ch) {//判断ch是否分隔符 //ch可能是:数字、字母、标点、空格、\0 //分隔符:!(数字、小写字母、I) ch = tolower(ch); if (ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch == 'I') return false; else return true; } bool isSeparator1(char ch) { ch = tolower(ch); if (ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch == 'I'||ch==' ') return false; else return true; } //若标点符号前面为空格,则删除 void go(string s) { char t[3001]; //全是I时达最长长度,加三倍 int i = 0, j; //i,j分别为s,t的下标 //i定位到s的第一个非空字符 for (; s[i] == ' '; ++i); //循环体为空 j = 0; //j的初值为0 while (s[i] != '\0') { if (s[i] == ' ' && s[i - 1] == ' ') { ++i; //如果漏了这句,有连续空格时会死循环 continue; } if (s[i] == '?') { t[j++] = '!'; ++i; continue; } if (s[i] != 'I') { t[j++] = tolower(s[i++]); continue; } t[j++] = s[i++]; //s[i]='I'的情况 若为me则改成you } t[j] = '\0'; j = 0; while (t[j] != '\0') { if (t[j] == 'I' && (j == 0 || isSeparator(t[j - 1])) && isSeparator(t[j + 1])) { cout << "you"; ++j; continue; } if (t[j] == 'm' && t[j + 1] == 'e' && (j == 0 || isSeparator(t[j - 1])) && isSeparator(t[j + 2])) { cout << "you"; j += 2; continue; } if ((j == 0 || isSeparator(t[j - 1])) && 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'&&isSeparator(t[j + 7])) { cout << "I can"; j += 7; continue; } if (t[j] == ' '&&iswhite(t[j + 1])) { j++; continue; } cout << t[j++]; } cout << endl; } int main() { string s; int n, i; cin >> n; getchar(); //吸收回车符 for (i = 1; i <= n; ++i) { getline(cin, s); cout << s<<endl; cout << "AI: "; go(s);//AI根据s输出对话 } return 0; }
感觉上升了一个层次之后自己思路还是颇为混乱,有时候甚至感觉还没上个学期直接在主函数里操纵顺畅舒心。。。
上个目标基本完成吧,尽力的挤压挤呀挤时间,说实话还是自己太菜了。。。有些地方要绕许久。
下一张学到树了,也是闻名许久的东西,非常重要。希望自己能好好掌握,另外将前面的东西融会贯通,摸索出一条适合自己思维线出来。