一道天梯赛搞得全场人崩溃的题,几乎现场就没几个人AC,现在回头看看,真的很考细节耐心地题目。
-----------------------------题目-----------------------------
- 无论用户说什么,首先把对方说的话在一行中原样打印出来;
- 消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
- 把原文中所有大写英文字母变成小写,除了 I;
- 把原文中所有独立的 I 和 me 换成 you;
- 把原文中所有的问号 ? 换成惊叹号 !;
- 把原文中所有独立的 can you 换成 I can —— 这里“独立”是指被空格或标点符号分隔开的单词;
- 在一行中输出替换后的句子作为 AI 的回答
输入样例:
6 Hello ? Good to chat with you can you speak Chinese? Really? Could you show me 5 What Is this prime? I,don 't know
输出样例:
Hello ? AI: hello! Good to chat with you AI: good to chat with you can you speak Chinese? AI: I can speak chinese! Really? AI: really! Could you show me 5 AI: could you show you 5 What Is this prime? I,don 't know AI: what Is this prime! you,don't know
-----------------------------题目-----------------------------
一、正式题解:
1* 函数原型声明:
void deleteSymbolSpace(); void deleteSpace(); void bigToSmall(); void changePersonY(); void changePersonI(); void changeQ(); bool isIndenpend(char temp);
deleteSymbolSpace —— 删除符号前的空格
deleteSpace —— 删除多余空格
bigToSmall —— 将大写转小写
changePersonY —— 改变人称You
changePersonI —— 改变人称I me
changeQ —— 问号改为感叹号
isIndenpend —— 判断是否独立函数
2* 主函数框架构建:
int main() { scanf("%d",×); getchar(); while(times--) { memset(temp,0,sizeof(temp)); memset(tMark,0,sizeof(tMark)); cin.getline(temp,2000); puts(temp); changeQ(); bigToSmall(); deleteSpace(); changePersonY(); changePersonI(); deleteSpace(); deleteSymbolSpace(); printf("AI: "); puts(temp); } return 0; }
抛开细节的实现方式,从大局角度思考程序的运行方式。
3*deleteSymbolSpace的实现:
void deleteSymbolSpace() { memset(dealing,0,sizeof(dealing)); count = 0; for(int i = 0;i) { if(temp[i] == 32) { int flag = 0; if(temp[i+1]>=33 && temp[i+1]<=47) flag = 1; else if(temp[i+1]>=58 && temp[i+1]<=64) flag = 1; else if(temp[i+1]>=91 && temp[i+1]<=96) flag = 1; else if(temp[i+1]>=123 && temp[i+1]<=126) flag = 1; if(flag == 1) { dealing[count] = temp[i+1]; count++; i=i+1; } else { dealing[count] = temp[i]; count++; } } else { dealing[count] = temp[i]; count++; } } dealing[count] = '\0'; memset(temp,0,sizeof(temp)); strcpy(temp,dealing); }
因为刚开始不知道判断字母和数字的函数,所以干脆直接简单粗暴,ASCII扫独立。
4* deleteSpace函数的实现:
void deleteSpace() { int start = 0; int space = 0; memset(dealing,0,sizeof(dealing)); count = 0; for(int i = 0;i) { if(start == 0 && temp[i] == 32) continue; else if(start == 0 && temp[i] != 32) { start = 1; dealing[count] = temp[i]; count++; continue; } if(temp[i] == 32) { if(space == 1) continue; if(tMark[i] == 1) continue; space = 1; dealing[count] = 32; count++; } else { space = 0; dealing[count] = temp[i]; count++; } } if(dealing[count-1] == 32) dealing[count-1] = '\0'; else dealing[count] = '\0'; memset(temp,0,sizeof(temp)); strcpy(temp,dealing); }
核心思路很简单,就是拿一个新数组去存新的结果,通过打标记判断是否是句头空行。
5* bigToSmall函数的实现:
void bigToSmall()//实际上可以用tolower代替 { for(int i = 0;i) { if( temp[i] >= 65 && temp[i] <= 90) { if(temp[i] == 'I') continue; temp[i] = temp[i] + 32; } } }
这里也是因为不知道有tolower这个魔鬼函数,所以依旧简单粗暴,暴力用ASCII解决问题。
6* changePersonY函数的实现:
void changePersonY() { for(int i = 0;i) { int flag = -1; if(i != 0 && !isIndenpend(temp[i-1])) continue; if(temp[i] == 'c' && temp[i+1] == 'a' && temp[i+2] == 'n') flag = 3; else if(temp[i] == 'c' && temp[i+1] == 'o' && temp[i+2] == 'u' && temp[i+3] == 'l' && temp[i+4] == 'd') flag = 5; if(flag != -1) { int mark = 0; if(temp[i+flag]>=32 && temp[i+flag]<=47) mark = 1; else if(temp[i+flag]>=58 && temp[i+flag]<=64) mark = 1; else if(temp[i+flag]>=91 && temp[i+flag]<=96) mark = 1; else if(temp[i+flag]>=123 && temp[i+flag]<=126) mark = 1; if(mark == 1) { if(temp[i+flag+mark] == 'y' && temp[i+flag+mark+1] == 'o' && temp[i+flag+mark+2] == 'u') { if(temp[i+flag+mark+3] != '\0' && !isIndenpend(temp[i+flag+mark+3])) continue; temp[i] = 'I'; tMark[i] = 1; temp[i+1] = temp[i+flag]; if(flag == 3) { temp[i+2] = 'c'; temp[i+3] = 'a'; temp[i+4] = 'n'; } else if(flag == 5) { temp[i+2] = 'c'; temp[i+3] = 'o'; temp[i+4] = 'u'; temp[i+5] = 'l'; temp[i+6] = 'd'; } temp[i +flag+2] = ' '; temp[i +flag+3] = ' '; tMark[i +flag+2] = tMark[i +flag+3] = 1; i = i +flag+3; } } } } }
转换人称是所有限制条件最麻烦最难的地方,需要注意打上标记,因为对string还是有点小反感,虽然挺好用,但是习惯用char进行单个强行处理,所以这里一直是用char来处理的~
7* changePersonI函数的实现:
void changePersonI() { memset(dealing,0,sizeof(dealing)); count = 0; for(int i = 0;i) { if(temp[i] == 'I' && isIndenpend(temp[i+1]) && tMark[i] == 0) { if(i != 0 && !isIndenpend(temp[i-1])) { dealing[count] = temp[i]; count++; continue; } dealing[count] = 'y'; dealing[count+1] = 'o'; dealing[count+2] = 'u'; count += 3; } else if(temp[i] == 'm' && temp[i+1] == 'e' && isIndenpend(temp[i+2])) { if(i != 0 && !isIndenpend(temp[i-1])) { dealing[count] = temp[i]; count++; continue; } dealing[count] = 'y'; dealing[count+1] = 'o'; dealing[count+2] = 'u'; count += 3; i = i + 1; } else { dealing[count] = temp[i]; count++; } } dealing[count] = '\0'; memset(temp,0,sizeof(temp)); strcpy(temp,dealing); }
这里尤其需要注意,不能把已经换过的人称再换一次,所以需要对标记进行判断。
8* changeQ的函数实现:
void changeQ() { for(int i = 0;i) if(temp[i] == '?') temp[i] = '!'; }
尽管函数简单,但尽量单独列出,使程序更加结构化。
9* isIndenpend函数的实现:
bool isIndenpend(char temp) { int mark = 0; if(temp>=32 && temp<=64) mark = 1; else if(temp>=91 && temp<=96) mark = 1; else if(temp>=123 && temp<=126) mark = 1; else if(temp == '\0') mark = 1; return mark; }
暴力ASCII码解决,虽然不是最佳的处理方式。
二、完整代码展示:
#include#include<string.h> #include using namespace std; //------------------------ void deleteSymbolSpace(); void deleteSpace(); void bigToSmall(); void changePersonY(); void changePersonI(); void changeQ(); bool isIndenpend(char temp); //------------------------ int times; char dealing[2001]; int count; char temp[2001]; int tMark[2001]; int main() { scanf("%d",×); getchar(); while(times--) { memset(temp,0,sizeof(temp)); memset(tMark,0,sizeof(tMark)); cin.getline(temp,2000); puts(temp); changeQ(); bigToSmall(); deleteSpace(); changePersonY(); changePersonI(); deleteSpace(); deleteSymbolSpace(); printf("AI: "); puts(temp); } return 0; } void deleteSymbolSpace() { memset(dealing,0,sizeof(dealing)); count = 0; for(int i = 0;i ) { if(temp[i] == 32) { int flag = 0; if(temp[i+1]>=33 && temp[i+1]<=47) flag = 1; else if(temp[i+1]>=58 && temp[i+1]<=64) flag = 1; else if(temp[i+1]>=91 && temp[i+1]<=96) flag = 1; else if(temp[i+1]>=123 && temp[i+1]<=126) flag = 1; if(flag == 1) { dealing[count] = temp[i+1]; count++; i=i+1; } else { dealing[count] = temp[i]; count++; } } else { dealing[count] = temp[i]; count++; } } dealing[count] = '\0'; memset(temp,0,sizeof(temp)); strcpy(temp,dealing); } void deleteSpace() { int start = 0; int space = 0; memset(dealing,0,sizeof(dealing)); count = 0; for(int i = 0;i ) { if(start == 0 && temp[i] == 32) continue; else if(start == 0 && temp[i] != 32) { start = 1; dealing[count] = temp[i]; count++; continue; } if(temp[i] == 32) { if(space == 1) continue; if(tMark[i] == 1) continue; space = 1; dealing[count] = 32; count++; } else { space = 0; dealing[count] = temp[i]; count++; } } if(dealing[count-1] == 32) dealing[count-1] = '\0'; else dealing[count] = '\0'; memset(temp,0,sizeof(temp)); strcpy(temp,dealing); } void bigToSmall()//实际上可以用tolower代替 { for(int i = 0;i ) { if( temp[i] >= 65 && temp[i] <= 90) { if(temp[i] == 'I') continue; temp[i] = temp[i] + 32; } } } void changePersonY() { for(int i = 0;i ) { int flag = -1; if(i != 0 && !isIndenpend(temp[i-1])) continue; if(temp[i] == 'c' && temp[i+1] == 'a' && temp[i+2] == 'n') flag = 3; else if(temp[i] == 'c' && temp[i+1] == 'o' && temp[i+2] == 'u' && temp[i+3] == 'l' && temp[i+4] == 'd') flag = 5; if(flag != -1) { int mark = 0; if(temp[i+flag]>=32 && temp[i+flag]<=47) mark = 1; else if(temp[i+flag]>=58 && temp[i+flag]<=64) mark = 1; else if(temp[i+flag]>=91 && temp[i+flag]<=96) mark = 1; else if(temp[i+flag]>=123 && temp[i+flag]<=126) mark = 1; if(mark == 1) { if(temp[i+flag+mark] == 'y' && temp[i+flag+mark+1] == 'o' && temp[i+flag+mark+2] == 'u') { if(temp[i+flag+mark+3] != '\0' && !isIndenpend(temp[i+flag+mark+3])) continue; temp[i] = 'I'; tMark[i] = 1; temp[i+1] = temp[i+flag]; if(flag == 3) { temp[i+2] = 'c'; temp[i+3] = 'a'; temp[i+4] = 'n'; } else if(flag == 5) { temp[i+2] = 'c'; temp[i+3] = 'o'; temp[i+4] = 'u'; temp[i+5] = 'l'; temp[i+6] = 'd'; } temp[i +flag+2] = ' '; temp[i +flag+3] = ' '; tMark[i +flag+2] = tMark[i +flag+3] = 1; i = i +flag+3; } } } } } void changePersonI() { memset(dealing,0,sizeof(dealing)); count = 0; for(int i = 0;i ) { if(temp[i] == 'I' && isIndenpend(temp[i+1]) && tMark[i] == 0) { if(i != 0 && !isIndenpend(temp[i-1])) { dealing[count] = temp[i]; count++; continue; } dealing[count] = 'y'; dealing[count+1] = 'o'; dealing[count+2] = 'u'; count += 3; } else if(temp[i] == 'm' && temp[i+1] == 'e' && isIndenpend(temp[i+2])) { if(i != 0 && !isIndenpend(temp[i-1])) { dealing[count] = temp[i]; count++; continue; } dealing[count] = 'y'; dealing[count+1] = 'o'; dealing[count+2] = 'u'; count += 3; i = i + 1; } else { dealing[count] = temp[i]; count++; } } dealing[count] = '\0'; memset(temp,0,sizeof(temp)); strcpy(temp,dealing); } void changeQ() { for(int i = 0;i ) if(temp[i] == '?') temp[i] = '!'; } bool isIndenpend(char temp) { int mark = 0; if(temp>=32 && temp<=64) mark = 1; else if(temp>=91 && temp<=96) mark = 1; else if(temp>=123 && temp<=126) mark = 1; else if(temp == '\0') mark = 1; return mark; }
三、个人反思:
AI这道题还有待进一步优化和升级,还有一个bug没有发现,还会继续解决,重点想讲的是程序设计的架构:
框架化的程序设计方法,是很早在Quanta冬令营中安卓JAVA的设计学习而得,由此类比过来的。我觉得,倘若要成为合格的程序设计师,出外工作的话,对框架的把握是非常重要的一个关卡,代码是否结构化明显,是否符合公司开发要求,是否易读都是程序设计过程中非常关键的地方。在安卓中,有分层WEB层、底层、数据层、素材动画层等等,选用合理的框架,能对团队的程序设计带来极大的效益,因此这个思想需要坚持并保留下来。
接下来的学习历程吧:
=》学习十字链表以及线性树、主席树、区间更新等ACM问题,为5月12日的广东省ACM省赛做准备
=》学习统计学习方法,掌握模型评估、决策树、向量机等知识,为数据挖掘实验室——多模态情感分析项目组努力。
=》前阵子七周都在英剧上花了不少时间,学习有些耽误,后续时间将慢慢追回学业,保证学业状态。
哦对了,顺便附加ACM中的KMP模板吧,挺好用的,直接背下来用就好了~
int KMP(char* str,char* pat) { int i,j,k; memset(fail,-1,sizeof(fail)); for(i = 1;pat[i];++i) { for(k = fail[i-1];k>=0 && pat[i] != pat[k+1];k = fail[k]); if(pat[k+1] == pat[i]) fail[i] = k + 1; } i = j = 0; while(str[i] && pat[j]) { if(pat[j] == str[i]) ++i,++j; else if(j == 0) ++i; else j = fail[j-1] + 1; } if(pat[j]) return 0; else return i-j+1; }