伪AI

7-2 AI核心代码 (30 分)

本题要求你实现一个简易版的 AI 英文问答程序,规则是:

  1. 无论用户说什么,首先把对方说的话在一行中原样打印出来;
  2. 消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
  3. 把原文中所有大写英文字母变成小写,除了 I;
  4. 把原文中所有独立的 I 和 me 换成 you;
  5. 把原文中所有的问号 ? 换成惊叹号 !;
  6. 把原文中所有独立的 can you 换成 I can —— 这里“独立”是指被空格或标点符号分隔开的单词;
  7. 在一行中输出替换后的句子作为 AI 的回答

 

  这道题是上机课上老师带着做到,看到这道题时,首先想到的时用if else或者case,但是看了一下要实现的内容,有太多的边界问题了。

  根据题目所给的规则,可以把处理的问题分为:空格、大写变小写(除了I)、独立的I和me改为you、?变!、独立的can you变为I can。

  按照老师的思路,首先从主函数开始写起,主函数主要是实现输入的功能。

 

int main()
{
    int n;
    string s;
    cin>>n;
    getchar();  //吸收回车 
    for(int i=1;i<=n;i++)
    {//输入对话 
        getline(cin,s);
        cout<<s<<endl;
        cout<<"AI: ";
        go(s);  //根据s输出AI的回答 
    }
    return 0; 
}
main

 

  根据main函数中构造调用的一个go函数,可以开始构思如何写go函数,其中go函数的功能主要是根据输入的内容,输出相应的内容。

 


     

       下面开始写go函数:

        一开始是定义string t,我们有选择地把我们需要输出的字符放到数组t中,到最后直接输出t数组的元素即可。但是这会出现问题,因为我们给t分配了空间,但是我们并不知道这个空间的首地址。所以改为定义一个字符数组t,根据题目,每一句不超过字符1000,那我们设的长度是不是1000或者1001就足够了呢?不是的,如果输入的字符全为I,那么长度为1000或1001显然是不够的,因此设定的长度应为3001。在处理前面根据规则分的五类的问题前,我们要先用一个空循环定位到字符串s的第一个非空元素,因为一句话最前面的空格我们是不需要输出的。

char t[3001];  //输入全为I时,输出的长度是输入的3倍 
    int i,j=0;  //i为s的下标,j为t的下标 
    for(i=0;s[i]!='\0'&&s[i]==' ';i++);  //定位到s的第一个非空(循环体为空) 
定位到非空元素

      定位到s的第一个非空元素后,我们先处理s中的空格的问题:在一句话中间有连续的空格

      方法:连续空格的一个特点是,当前扫描字符是空格,前一个也是空格,这时,除了第一个空格可以放进数组t以外,其他空格都不需要放进数组t里。

if(s[i]==' '&&s[i-1]==' ')   
        {//连续空格时,只copy一个空格
            i++;
            continue;
        }
解决空格问题

     解决?→!的问题

     方法:当前扫描字符为?时,放进数组t的字符为!。

if(s[i]=='?')  //解决?->! 
        {
            t[j++]='!';
            i++; 
            continue;
        } 
解决?的问题

     解决大写转小写的问题(除了I)

     方法,先判断当前元素是否为I,如果是,则放进数组t,如果不是,则先转换成小写,再放进数组t里

if(s[i]!='I')  
        {//如果当前的元素不是I就大小写并转换 
            t[j]=tolower(s[i]);  //大写转小写 
            i++;
            j++; 
            continue;
        }   
        else t[j++]=s[i++]; //当s[i]=I时,直接写入t数组
大写转小写

     处理完字符串s后,此时的数组t已经是处理完连续空格、?→!、大写转小写的字符串s了,这时应该在数组t的后面加个结尾符。

     此时的数组t是否就是我们想要的最后结果呢?当然不是,空格问题我们只处理了连续空格还有开头空格的问题,还有分割符前的空格以及每句话最后的空格我们还没有处理。

     处理分隔符前空格的方法:判断条件是:当前扫描字符为空格,且下一个扫描的字符为分割符。

     处理每句话最后的空格的方法:判断条件:当前扫描字符为空格,且下一个扫描的字符为结尾符。

if(t[j]==' ' && inDependent(t[j+1]))   
        {//分隔符前没有空格
            j++;
            continue;
        }
if(s[j]==' '&&s[j+1]=='\0')
        {//处理最后一个空格 
            j++;
            continue;
        }
处理剩余的空格问题

     在处理剩余空格问题时,用到了一个判断是否为分隔符的函数

bool inDependent(char ch)
{//判断ch是否为分隔字符 
    ch=tolower(ch);  //把大写转换成小写,便于比较 
    if(ch>= '0' && ch<='9' || ch>='a' && ch<='z')  return false;
    else return true;
}
判断是否为分隔字符

    处理完剩余的空格问题后,开始处理独立的I和me转换成you

    I转换成you的方法:判断条件:当前扫描字符为I,同时前一个字符以及后一个字符为分隔符。但是这时有可能会发生越界,当字符的第一个就是I时,此时j-1就成了-1,发生了越界。因此在判断I的前一个字符是否为分隔符时,首先判断I是否为第一个字符。而I的下一个字符处不用担心越界问题,因为就算I是最后一个字符,在I的后面还有一个结尾符,因此不会发生越界。

    me转换成you的方法:判断条件:当前扫描字符为m,以及下一个扫描字符为e,同时前一个字符以及后一个字符为分隔符。me和上面I的情况一样,有可能会发生越界问题。要做相同的处理

if(t[j]=='I' && (j==0 || inDependent(t[j-1])) && inDependent(t[j+1]))  //独立的I转成you 
        {//把独立的I转换成you,要考虑I就是第一个字符的情况,j=0处有可能发生越界 
            cout<<"you" ;
            j++;
            continue;
        } 
        if(t[j]=='m' && t[j+1]=='e' && (j==0 || inDependent(t[j-1])) && inDependent(t[j+2]))  //独立的me转成you
        {//把独立的me转换成you,同样要考虑y是第一个字符的情况,j=0有可能发生越界 
            cout<<"you";
            j=j+2;  //右移两个字符,判断了m、e两个字符 
            continue;
        }
        if(t[j]==' ' && inDependent(t[j+1]))  
独立的I和me转换成me

   此时只剩下独立的can you转换成I can的问题了。

   方法:先判断是否为can you,再判断是否独立,同样要考虑越界的问题。

if(iscanyou(t,j)&&(j==0||inDependent(t[j-1]))&&inDependent(t[j+7]))
        {//把can you →I can 
            cout<<"I can";
            j=j+7;  //can you一共有七个字符 
            continue;
        }
独立的can you转换成I can

   在判断是否为can you时用到了一个iscanyou的函数。

bool iscanyou(char t[],int j)
{//判断是否为can you 
    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')
    return true;
    else return false;
}
iscanyou

 


 

   全部代码如下:

#include <iostream>
#include <string>
#include <cstdio>
using namespace std; 
bool inDependent(char ch)
{//判断ch是否为分隔字符 
    ch=tolower(ch);  //把大写转换成小写,便于比较 
    if(ch>= '0' && ch<='9' || ch>='a' && ch<='z')  return false;
    else return true;
}

bool iscanyou(char t[],int j)
{//判断是否为can you 
    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')
    return true;
    else return false;
}
void go(string s)
{//根据s输出AI的回答
    char t[3001];  //输入全为I时,输出的长度是输入的3倍 
    int i,j=0;  //i为s的下标,j为t的下标 
    for(i=0;s[i]!='\0'&&s[i]==' ';i++);  //定位到s的第一个非空(循环体为空)  
    while(s[i]!='\0')  //把s串copy到t
    {
        if(s[i]==' '&&s[i-1]==' ')  //连续空格时,只copy一个空格 
        {
            i++;
            continue;
        }
        if(s[i]=='?')  //解决?->! 
        {
            t[j++]='!';
            i++; 
            continue;
        } 
        if(s[i]!='I')  
        {//如果当前的元素不是I就大小写并转换 
            t[j]=tolower(s[i]);  //大写转小写 
            i++;
            j++; 
            continue;
        }   
        else t[j++]=s[i++]; //当s[i]=I时,直接写入t数组 
    } //while 
    t[j]='\0';    //给t数组补上结尾符
    j=0;
    while(t[j]!='\0')  
    {
        if(t[j]=='I' && (j==0 || inDependent(t[j-1])) && inDependent(t[j+1]))  //独立的I转成you 
        {//把独立的I转换成you,要考虑I就是第一个字符的情况,j=0处有可能发生越界 
            cout<<"you" ;
            j++;
            continue;
        } 
        if(t[j]=='m' && t[j+1]=='e' && (j==0 || inDependent(t[j-1])) && inDependent(t[j+2]))  //独立的me转成you
        {//把独立的me转换成you,同样要考虑y是第一个字符的情况,j=0有可能发生越界 
            cout<<"you";
            j=j+2;  //右移两个字符,判断了m、e两个字符 
            continue;
        }
        if(t[j]==' ' && inDependent(t[j+1]))   
        {//分隔符前没有空格
            j++;
            continue;
        }
        if(iscanyou(t,j)&&(j==0||inDependent(t[j-1]))&&inDependent(t[j+7]))
        {//把can you →I can 
            cout<<"I can";
            j=j+7;  //can you一共有七个字符 
            continue;
        }
        if(s[j]==' '&&s[j+1]=='\0')
        {//处理最后一个空格 
            j++;
            continue;
        }
        cout<<t[j];  //输出 
        ++j;
    }//while
    cout<<endl;
}

int main()
{
    int n;
    string s;
    cin>>n;
    getchar();  //吸收回车 
    for(int i=1;i<=n;i++)
    {//输入对话 
        getline(cin,s);
        cout<<s<<endl;
        cout<<"AI: ";
        go(s);  //根据s输出AI的回答 
    }
    return 0; 
}
总代码

 


   

   刚开始看到这道题时,觉得很难,无从下手。老师带着我们从主函数写起,再返回去写主函数中调用的函数,同时还把规则中要处理的问题分成具体的五类问题,五类问题又细分了,这样一个问题一个问题地解决就好解决了。

   上次目标是做题速度快一点,这一次可能是题目简单?确实比上一次快了。再接再厉!

   接下来的目标:解决问题先从主函数开始想。

 

 

 

 

 

 

 

 

posted on 2019-04-14 14:58  ChrisMua  阅读(286)  评论(1编辑  收藏  举报