大侦探福尔摩斯接到一张奇怪的字条:
我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm。
大侦探很快就明白了,字条上奇怪的乱码实际上就是约会的时间星期四 14:04,因为前面两字符串中第 1 对相同的大写英文字母(大小写有区分)是第 4 个字母 D,代表星期四;第 2 对相同的字符是 E ,那是第 5 个英文字母,代表一天里的第 14 个钟头(于是一天的 0 点到 23 点由数字 0 到 9、以及大写字母 A 到 N 表示);后面两字符串第 1 对相同的英文字母 s 出现在第 4 个位置(从 0 开始计数)上,代表第 4 分钟。现给定两对字符串,请帮助福尔摩斯解码得到约会的时间。
输入格式:
输入在 4 行中分别给出 4 个非空、不包含空格、且长度不超过 60 的字符串。
输出格式:
在一行中输出约会的时间,格式为 DAY HH:MM,其中 DAY 是某星期的 3 字符缩写,即 MON 表示星期一,TUE 表示星期二,WED 表示星期三,THU 表示星期四,FRI 表示星期五,SAT 表示星期六,SUN 表示星期日。题目输入保证每个测试存在唯一解。
输入样例:
3485djDkxh4hhGE
2984akDfkkkkggEdsb
s&hgsfdk
d&Hyscvnm
输出样例:
THU 14:04
题意分析
实际上这道题目并不难,本质上只是简单的字符查找比较输出,但题目的描述着实让人思路有点乱。尤其是第二个条件里的"第二对相同的字符",到底是从头到尾比较的第二对还是再第一个条件的基础上的第二对呢?经过测试发现应该是第一个条件的基础上的第二对。了解这一点后开始解题。
我在做题的时候思绪比较凌乱,于是产生了下面好几种做法,下面我分析一下我的各种奇葩做法吧!尽管这样,每种做法本质都是简单比较。我们采用的编译器是g++ 6.5.0版本。注意此题尽量不要使用cout输出 cin读入防止超时。另外读入字符串请不要用gets这种不安全的函数,干脆直接废弃不用。本博文末尾将会讲到可供替代的写法。
做法1
#include
int main()
{
char date[4][61];
int day,hour,min;
char week[7][4] = {
"MON","TUE","WED",
"THU","FRI","SAT",
"SUN"
};
int count = 1;
scanf("%s%s%s%s", date[0], date[1], date[2], date[3]);
for(int i = 0;date[0][i] != '\0' && date[1][i] != '\0';i++)
{
if(date[0][i] >= 'A' && date[0][i] <= 'G' && date[0][i] == date[1][i] && count == 1)
{
day = date[0][i]-'A';
++count;
}
else if(count == 2 && date[0][i] == date[1][i] && (date[0][i] >= '0' && date[0][i] <= '9' || date[0][i] >= 'A' && date[0][i] <= 'N'))
{
if(date[0][i] >= '0' && date[0][i] <= '9')
hour = date[0][i]-'0';
else
hour = date[0][i]-'A'+10;
break;
}
}
for(int i = 0;date[2][i] != '\0' && date[3][i] != '\0';i++)
{
if((date[2][i] >= 'a' && date[2][i] <= 'z' || date[2][i] >= 'A' && date[2][i] <= 'Z') && date[2][i] == date[3][i])
{
min = i;
break;
}
}
printf("%s %02d:%02d", week[day], hour, min);
return 0;
}
先来说说这种做法的优势
首先这种做法意图很明确,分别求出三组时间(其中day是下标),再集中输出,注意day的获取需要限定在A–G范围内!!! hour需要在0–9或者A–N范围内!!!还有个小细节, “||“运算符优先级低于”&&”,需要括号括起,否则容易导致输入错误。
这种做法的缺点也很明显
程序略微冗余,实际上我们通过增加头文件可以改写部分程序如下
方法1改写
#include
#include
int main()
{
char date[4][61];
int day,hour,min;
char week[7][4] = {
"MON","TUE","WED",
"THU","FRI","SAT",
"SUN"
};
int count = 1;
scanf("%s%s%s%s", date[0], date[1], date[2], date[3]);
for(int i = 0;date[0][i] != '\0' && date[1][i] != '\0';i++)
{
if(date[0][i] >= 'A' && date[0][i] <= 'G' && date[0][i] == date[1][i] && count == 1)
{
day = date[0][i]-'A';
++count;
}
else if(count == 2 && date[0][i] == date[1][i] && (isdigit(date[0][i]) || date[0][i] >= 'A' && date[0][i] <= 'N'))
{
if(isdigit(date[0][i]))
hour = date[0][i]-'0';
else
hour = date[0][i]-'A'+10;
break;
}
}
for(int i = 0;date[2][i] != '\0' && date[3][i] != '\0';i++)
{
if(isalpha(date[2][i]) && date[2][i] == date[3][i])
{
min = i;
break;
}
}
printf("%s %02d:%02d", week[day], hour, min);
return 0;
}
粗略分析下时间复杂度为O(n),不使用cin和cout不会超时。
方法二
这种方法在每组得到结果后直接输出。
#include
#include
int main()
{
char date[4][61];
int i;
int len1,len2,len3,len4;
char week[7][4] = {
"MON","TUE","WED",
"THU","FRI","SAT",
"SUN"
};
scanf("%s%s%s%s", date[0], date[1], date[2], date[3]);
len1 = strlen(date[0]);
len2 = strlen(date[1]);
len3 = strlen(date[2]);
len4 = strlen(date[3]);
for(i = 0;i < len1 && i < len2;i++)
{
if(date[0][i] == date[1][i] && date[0][i] >= 'A' && date[0][i] <= 'G')
{
printf("%s ", week[date[0][i]-'A']);
break;
}
}
for(i++;i < len1 && i < len2;i++)
{
if(date[0][i] == date[1][i])
if(date[0][i] >= '0' && date[0][i] <= '9')
{
printf("%02d:", date[0][i]-'0');
break;
}
else if(date[0][i] >= 'A' && date[0][i] <= 'N')
{
printf("%d:", date[0][i]-'A'+10);
break;
}
}
for(i = 0;i < len3 && i < len4;i++)
if(date[2][i] == date[3][i] && (date[2][i] >= 'a' && date[2][i] <= 'z' || date[2][i] >= 'A' && date[2][i] <= 'Z'))
{
printf("%02d", i);
break;
}
return 0;
}
这种做法和上面的做法在时间上没啥大区别,但是更加整洁美观,我个人倾向解法二。
替代gets()函数!!!!
首先我们讲讲gets函数为什么不安全:
我们的程序读入是以输入流的形式,假定我们有个字符数组容量为10,而gets函数读入的字符长度往往不确定,这时如果某个流长度超过了10,都会导致多余的部分超出数组范围而对计算机内其余数据存在擦写的隐患,这在有些情况下是灾难性的。所以C11直接强硬地废弃了gets函数。你可能会问:strcpy这一类的函数也有安全隐患,但是为什么不被废弃呢?这是因为strcpy的操作是程序员决定的,C语言要求程序员自己保证程序的安全性,gets函数的不安全性来自使用程序的用户而非程序员。
我们可以这样替代gets()函数
1.scanf()读入空格或者换行等空白字符分隔的多个字符串或者不含空格的字符串
比如:
上题读入四个字符串,每个字符串用换行符分割,我们就可以采用这种方式读入。
这样做的可行性在于scanf函数对一个字符串读入从第一个非空白字符开始,到第二个空白字符结束。(%c读入时特殊,它会读入空白字符)
2.fgets()函数读入字符串,它需要三个参数,格式:fgets(字符串首地址,字符串最大长度,读入文件)
一般在PTA中我们往往这样写(假设字符串为char str[15]):fgets(str,14,stdin);
注意fgets()函数会保存可能存在的换行符,如果题目指明了每个字符串以换行符结尾,那么上面所说的例子应该改为char str[16],fgets(str,15,stdin),str[14] = '\0’原因在于换行符占用了一个位置。stdin的意思时standard input(标准输入)。
关于fgets()函数的例子可以查看博主博文PTA乙级说反话