PTA L1-064 估值一亿的AI核心代码(详解)

前言:内容包括:题目,代码实现,大致思路,代码解读

题目:

以上图片来自新浪微博。

本题要求你实现一个稍微更值钱一点的 AI 英文问答程序,规则是:

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

输入格式:

输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。

输出格式:

按题面要求输出,每个 AI 的回答前要加上 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: I could show you 5
What Is this prime? I,don 't know
AI: what Is this prime! you,don't know

代码实现: 

#include<stdio.h>
#include<ctype.h>
//判断是否为空格or非字母非数字字符
int Judge(char p)
{
	if ((p >= 'A' && p <= 'Z') || (p >= 'a' && p <= 'z') || (p >= '0' && p <= '9'))
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

//去除空格
void DelBlank(char arr[], int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		//处理开头的空格
		if (arr[0] == ' ')
		{
			int j = 0;
			for (j = 0; j < len; j++)
			{
				arr[j] = arr[j + 1];
			}
		}

		if (arr[i] == ' ')
		{
			int flag = Judge(arr[i + 1]);
			if (flag == 1)
			{
				int k = 0;
				for (k = i; k < len; k++)
				{
					arr[k] = arr[k + 1];
				}
				i--;
			}
		}
	}
}

//大写转小写
void Tolower(char arr[], int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		if (arr[i] != 'I')
		{
			arr[i] = tolower(arr[i]);
		}
	}
}

//替换并打印最终结果
void ReplacePrint(char arr[])
{
	printf("AI: ");

	int i = 0;
	for (i = 0; i < strlen(arr); i++)
	{
		if ((arr[i] == 'c' && arr[i + 1] == 'a' && arr[i + 2] == 'n' && arr[i + 3] == ' ' && arr[i + 4] == 'y' && arr[i + 5] == 'o' && arr[i + 6] == 'u') && (i == 0 || arr[i - 1] == ' ') && (arr[i + 7] == ' ' || arr[i + 7] == '\0' || Judge(arr[i + 7])))
		{
			printf("I can");
			i += 6;
		}
		else if ((arr[i] == 'c' && arr[i + 1] == 'o' && arr[i + 2] == 'u' && arr[i + 3] == 'l' && arr[i + 4] == 'd' && arr[i + 5] == ' ' && arr[i + 6] == 'y' && arr[i + 7] == 'o' && arr[i + 8] == 'u') && (i == 0 || arr[i - 1] == ' ') && (arr[i + 9] == ' ' || arr[i + 9] == '\0' || Judge(arr[i + 9])))
		{
			printf("I could");
			i += 8;
		}
		else if (arr[i] == 'I' && (i == 0 || arr[i - 1] == ' ') && (arr[i + 1] == ' ' || arr[i + 1] == '\0' || Judge(arr[i + 1])))
		{
			printf("you");
		}
		else if ((arr[i] == 'm' && arr[i + 1] == 'e') && (i == 0 || arr[i - 1] == ' ') && (arr[i + 2] == ' ' || arr[i + 2] == '\0' || Judge(arr[i + 2])))
		{
			printf("you");
			i += 1;
		}
		else
		{
			printf("%c", arr[i]);
		}
	}
}

void Change(char arr[], int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		if (arr[i] == '?')
		{
			arr[i] = '!';
		}
	}
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	getchar();
	char arr[1001] = { 0 };
	int i = 0;
	for (i = 0; i < n; i++)
	{
		gets(arr);
		printf("%s\n", arr);
		int len = strlen(arr);
		DelBlank(arr, len);
		Tolower(arr, len);
		Change(arr, len);
		ReplacePrint(arr);
		printf("\n");
	}
	return 0;
}

大致思路:

1 去除空格 : 分两种空格 

 a. 不在开头的空格

遍历字符数组,当发现当前元素是一个空格,需要判断这个元素后面的另一个元素是否为空格or非数字非字母字符,若是,则需要消除多余空格,操作是覆盖法,让这个为空格的元素的后面的所有元素都往前挪一个位置,构成覆盖效果

比如:Hello ?

?前面是一个空格,这种空格是需要被删除的,即一个不需要删除的空格的后面可以是字母,数字,需要删除的空格它的后面是非数字非字母字符,空格

show me 5:第一个空格后面是字母,合法,第二个空格后面是数字,合法,这些空格都不需要删除

Hello ?        这个空格的后面是非数字非字母字符,非法,此空格需要删除

b. 在最开头的空格:可能是一个or多个

 无论是一个or多个空格出现在最开头我们都需要全部消除,若是存在多个空格且最后一个空格后面紧跟着的是字母,则a中所提的消除空格的代码不会执行,故而我们要将出现在开头的空格的消除单独再使用这个覆盖法:让这个为空格的元素的后面的所有元素都往前挪一个位置

2 大写转小写

遍历整个字符数组,所有大写英文字母变成小写,除了 I

可以使用库函数tolower

3 ?变成!

4 替换独立的 can youcould you ->I canI could

        I 和 me 换成 you

在字符数组中一个字符一个字符的对应can youcould you ,I , me

对应之后,不打印can youcould you ,而是打印I canI could

      不打印I , me,而是打印you

若是字符数组中没有完整连续对应上述这些内容,则正常一个一个输出字符

若是完整连续对应上了,在替换打印后,需要要打印can you,could you,I , me之后剩下的所有字符

 代码解读

主体部分

int main()
{
	int n = 0;
	scanf("%d", &n);
	getchar();//读取数字之后的换行符\n
	char arr[1001] = { 0 };
	int i = 0;
	for (i = 0; i < n; i++)
	{
		gets(arr);
		printf("%s\n", arr);
		int len = strlen(arr);
		DelBlank(arr, len);//删除多余空格
		Tolower(arr, len);//大写转成小写
		Change(arr, len);//?->!
		ReplacePrint(arr);//替换can you、could you,I , me并输出已调整好只需替换某些内容的字符串
		printf("\n");
	}
	return 0;
}

 使用get读取带有空格的字符串,,注意需要使用getchar读取\n

函数部分

part 1 删除多余空格

void DelBlank(char arr[], int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		//处理开头的空格
		if (arr[0] == ' ')
		{
			int j = 0;
			for (j = 0; j < len; j++)
			{
				arr[j] = arr[j + 1];
			}
		}

		if (arr[i] == ' ')
		{
			int flag = Judge(arr[i + 1]);
			if (flag == 1)
			{
				int k = 0;
				for (k = i; k < len; k++)
				{
					arr[k] = arr[k + 1];
				}
				i--;//覆盖掉当前为空格的元素后,需要i--,因为在循环中下一步就是执行i++,先退一步,再前进一步==原地不动,就能再次判断这个位置是否覆盖的新值仍为空格
			}
		}
	}
}

空格分为:字符串开头处的空格非开头处的空格

非开头处空格的处理:

遍历整个字符数组,若是当前元素是空格,且进一步判断是需要删除的空格,则使用覆盖法,用它后面的人来覆盖它,即此空格后面的所有元素都往前移动一位

注意:由于存在连续空格的情况,即检查出当前元素是空格,使用覆盖法,这个空格位置覆盖上来的又是一个空格,不能直接++,跳过这个已经检查过的位置,判断下一个位置,而是需要先–,这样就又能重新判断这个被覆盖上新值的位置是否仍为空格

a. 判断出当前元素是空格,下一步就是判断这个空格是否需要删除

一个需要删除的空格的条件:它后面的一个元素不是字母,不是数字,即它后面的元素是非字母非数字字符

Judge函数就是判断一个元素是否为非字母非数字字符,返回值为1表示此元素是非字母非数字字符,返回0表示此元素是数字或者字母

开头处空格的处理:

开头处空格比较特殊,都需要删除,而上面所提的删除空格的代码执行的条件是当前空格后面的一个元素需要是非数字非字母字符

而开头处的空格,它后面的一个元素可能是字母,故而不能满足这个条件,若是它两统统都使用着一个删除空格的代码,则肯定是行不通的

我们另外设计一个专门删除开头处的空格的代码,只要开头处(即字符数组的首元素arr[0])是空格,我们就会删除掉这个空格:

        if (arr[0] == ' ')
		{
			int j = 0;
			for (j = 0; j < len; j++)
			{
				arr[j] = arr[j + 1];
			}
		}

part 2 大写转小写

//大写转小写
void Tolower(char arr[], int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		if (arr[i] != 'I')
		{
			arr[i] = tolower(arr[i]);
		}
	}
}

遍历整个字符数组,使用tolower函数将大写字母转成小写字母

tolower函数的头文件:

#include <ctype.h>

part 3 ?->!

void Change(char arr[], int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		if (arr[i] == '?')
		{
			arr[i] = '!';
		}
	}
}

part 4 输出已经调整好的只需替换某些内容的字符串

//替换并打印最终结果
void ReplacePrint(char arr[])
{
	printf("AI: ");

	int i = 0;
	for (i = 0; i < strlen(arr); i++)
	{
		if ((arr[i] == 'c' && arr[i + 1] == 'a' && arr[i + 2] == 'n' && arr[i + 3] == ' ' && arr[i + 4] == 'y' && arr[i + 5] == 'o' && arr[i + 6] == 'u') && (i == 0 || arr[i - 1] == ' ') && (arr[i + 7] == ' ' || arr[i + 7] == '\0' || Judge(arr[i + 7])))
		{
			printf("I can");
			i += 6;//要打印can you之后的剩余字符,从下标i+=7开始,所以先将i(此时是c的下标)加上6(即u的下标,马上要执行的下一条语句是i++,就能使得i成为can you之后的剩余字符的第一个字符的下标
		}
		else if ((arr[i] == 'c' && arr[i + 1] == 'o' && arr[i + 2] == 'u' && arr[i + 3] == 'l' && arr[i + 4] == 'd' && arr[i + 5] == ' ' && arr[i + 6] == 'y' && arr[i + 7] == 'o' && arr[i + 8] == 'u') && (i == 0 || arr[i - 1] == ' ') && (arr[i + 9] == ' ' || arr[i + 9] == '\0' || Judge(arr[i + 9])))
		{
			printf("I could");
			i += 8;//要打印could you之后的剩余字符,从下标i+=9开始,所以先将i(此时是c的下标)加上8(即u的下标,马上要执行的下一条语句是i++,就能使得i成为could you之后的剩余字符的第一个字符的下标
		}
		else if (arr[i] == 'I' && (i == 0 || arr[i - 1] == ' ') && (arr[i + 1] == ' ' || arr[i + 1] == '\0' || Judge(arr[i + 1])))
		{
			printf("you");
		}
		else if ((arr[i] == 'm' && arr[i + 1] == 'e') && (i == 0 || arr[i - 1] == ' ') && (arr[i + 2] == ' ' || arr[i + 2] == '\0' || Judge(arr[i + 2])))
		{
			printf("you");
			i += 1;//要打印me之后的剩余字符,从下标i+=2开始,所以先将i(此时是c的下标)加上1(即e的下标,马上要执行的下一条语句是i++,就能使得i成为me之后的剩余字符的第一个字符的下标
		}
		else
		{
			printf("%c", arr[i]);
		}
	}
}

在字符数组中若不能找到完整连续的can you,could you, I ,me,则一个字符一个字符地输出内容

这是无需替换的正常字符

若是字符数组中能找到完整连续的can you, could you ,I me,则打印I canI could,you,代替can you, could you ,I me的打印

判断完整连续的独立can you:

can you可以出现在字符串的开头/末尾/中间

独立(隔开)can you: 分为前和后两部分,就像两条线将can you 围住

前一条线: 字符c的下标是0(can you 出现在开头) 或者 字符c的前一个元素是空格(can you 出现在字符串中)

后一条线: 字符u的后一个元素是空格or标点符号or \0

could you ,I me的独立判断同上

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年11月13日
下一篇 2023年11月13日

相关推荐