⌛️ 两岸猿声啼不住,三千单词万重关
文章目录
笨小猴 🐵
上一题链接: C/C++百题打卡[1/100]——扫雷 ⭐️⭐️ 考查数组.
下一题链接: C/C++百题打卡[3/100]——约瑟夫问题⭐️⭐️⭐️ 考查链表.
百题打卡总目录: 🚧 🚧 …
一、题目总述
● 笨小猴的词汇量很小,考四六级时很头疼。但是他找到了一种方法,用这种方法去盲选很大几率对!这种方法的具体描述如下:假设 m a x n maxn maxn 是该单词中,某一字母出现的最多次数。 m i n n minn minn 是某该单词中,某一字母出现最少次数。如果 m a x n − m i n n maxn-minn maxn−minn 的结果是一个质数,那么笨小猴就认为这是个 “ L u c k y W o r d Lucky \,\, Word LuckyWord” ,这样的单词很可能就是正确的答案。
● 输入描述:输入一行,是一个单词,其中只可能出现小写字母,并且长度小于 100 100 100。
● 输出描述:
① 输出两行,第一行是一个字符串,若该单词是个 “
L
u
c
k
y
W
o
r
d
Lucky \,\, Word
LuckyWord” ,则输出
L
u
c
k
y
W
o
r
d
Lucky \,\, Word
LuckyWord ,否则输出
N
o
A
n
s
w
e
r
No \,\, Answer
NoAnswer 。
② 第二行是一个整数,如果输入单词是个 “
L
u
c
k
y
W
o
r
d
Lucky \,\, Word
LuckyWord” ,输出
m
a
x
n
−
m
i
n
n
maxn-minn
maxn−minn 的值,否则输出
0
0
0 。
运行限制:最大运行时间: 1 s 1s 1s,最大运行内存: 128 M 128M 128M
● 输入样例一:
error
● 输出样例一:
Lucky Word
2
● 输入样例二:
Olympic
● 输出样例二:
No Answer
0
二、思考空白区
● 题目难度:⭐️⭐️
● 建议思考时间:⌛️
三、题目解析
● 这是一道小型综合题
,只要细分好模块,一个一个地解决就 OK 了。
● 算法设计:
[1]
设计好 输入输出模块 。
[2]
设计好 打表模块 。
[3]
设计好 过渡模块 。
[4]
设计好 质数判断模块 。
3.1 第[1]步:输入输出模块
● 这是手到拈来的事:
#include <stdio.h>
int main()
{
/* 输入模块 */
char str[105];
scanf("%s", str);
/* 中间算法设计模块 */
...
/* 输出模块 */
if ( ...是质数 )
{
cout << "Lucky Word" << endl;
cout << num << endl;
}
else
{
cout << "No Answer" << endl;
cout << 0 << endl;
}
return 0;
}
3.2 第[2]步:打表模块
● 根据题目要求,我们要记录一个单词中,出现最多的字母的次数 和 出现最少字母的次数。
● 而总所周知,字母总共用26
个,算上大写的话,就总共有52
个。
● 所以我们需要开一张空间≥52
的表,来记录这个单词中所有字母,各自出现的次数。
● 故我们要记录A~Z
,和a~z
的出现次数。它们在 ASCII 码表的位置分别如下:
● 代码如下:【注:strlen()
函数可以计算出字符串的有效长度。需包含头文件string.h
】
#include<stdio.h>
#include<string.h>
...
/* 输入模块 */
...
/* 打表模块 */
int table[60]; // 空间只要 >= 52 就行
for( int i = 0; i < 60; i++ ) // 表的初始化
table[i] = 0;
int len = strlen(str); // len: 代码界的常用术语, 表示某一字符串的长度
for( int i = 0; i < len; i++ )
{
if ('a' <= str[i] && str[i] <= 'z')
{
int ind = str[i] - 'a'; // 减 'a' 相当于减去了 97 , 减的是 ASCII m码
table[ind]++;
}
else if ('A' <= str[i] && str[i] <= 'Z')
{
int ind = str[i] - 'A'; // 比如说: 'C' - 'A' 的结果为 2, 'A' - 'A' = 0
table[ind + 26]++; // 前 26 个位置存储小写字母的出现次数, 后 26 个位置...大写...次数
}
}
/* 输出模块 */
if ( ...是质数 )
{ ... }
else
{ ... }
3.3 第[3]步:过渡模块
● 既然表已经打好后,那我们接着要找出,出现最多的字母的次数maxn
和出现最少字母的次数minn
。机理也很简单,遍历一遍,一个一个比就行。
#include<stdio.h>
#include<string.h>
...
/* 输入模块 */
...
/* 打表模块 */
...
/* 过渡模块 */
int maxn = 0;
int minn = 99999;
for( int i = 0; i < 52; i++ ) // 总共 52 个字母
{
if (table[i] == 0) // 这是一个小细节, 当某个单词都没有出现时, 值为 0 时, 直接跳过
continue;
if( table[i] > maxn )
maxn = table[i];
else if( table[i] < minn )
minn = table[i];
}
int num = maxn - minn;
/* 输出模块 */
if ( num 是质数 )
{ ... }
else
{ ... }
3.4 第[4]步:质数判断模块
● 质数:指在大于1
的自然数中,除了1
和它本身以外不再有其他因数的自然数。
● 究其根本: 质 数 ≠ a × b 质数≠a \times b 质数=a×b。其中, a , b ≠ 1 或 它 本 身 a,b≠1或它本身 a,b=1或它本身
● 故一种简单的判断机制是:从a=2
开始,让Number
去求余,直到a=Numer-1
,只要余数全都不为0
,则说明该数是质数,代码如下:
int Prime_Judge(int Number) // Prime: 主要的;典型的;素数的
{
if (Number == 0 || Number == 1) // 这两句代码容易被忽略, 注意 0 和 1 都不是质数
return 0;
for (int i = 2; i <= Number-1; i++)
{
if (Number % i == 0) // 能被整除说明不是质数(也称为素数)
return 0;
}
return 1;
}
● 但其实,只要我们对 “
质
数
≠
a
×
b
质数≠a \times b
质数=a×b。其中,
a
,
b
≠
1
或
它
本
身
a,b≠1或它本身
a,b=1或它本身 ” 这个公式进行细想,就会发现,其实我们只要我们从a=2
一直判断到
a
=
N
u
m
b
e
r
a=\sqrt{Number}
a=Number 即可。
● 因为 N u m b e r = N u m b e r × N u m b e r Number=\sqrt{Number} \times \sqrt{Number} Number=Number×Number,当 a > N u m b e r a>\sqrt{Number} a>Number 时,再求余就是在做多余的操作。
● 最后,在C/C++
里面,开根号需要一定的计算开销。我们将其优化为如下代码:
int Prime_Judge(int Number)
{
if (Number == 0 || Number == 1)
return 0;
for (int i = 2; i * i <= Number; i++)
{
if (Number % i == 0) // 能被整除说明不是质数(也称为素数)
return 0;
}
return 1;
}
● 至此,整个问题得以解决。
四、做题小结与反思
● 熟悉了一下 打表操作 。
● 熟悉并优化了一下 质数判断机制 。
五、完整代码(C和C++版)
● C 语言版本:
#include<stdio.h>
#include<string.h>
/* 质数模块 */
int Prime_Judge(int Number)
{
if (Number == 0 || Number == 1)
return 0;
for (int i = 2; i * i <= Number; i++)
{
if (Number % i == 0) // 能被整除说明不是质数(也称为素数)
return 0;
}
return 1;
}
int main()
{
/* 输入模块 */
char str[105];
scanf("%s", str);
/* 打表模块 */
int table[60]; // 空间只要 >= 52 就行
for( int i = 0; i < 60; i++ ) // 表的初始化
table[i] = 0;
int len = strlen(str); // len: 代码界的常用术语, 表示某一字符串的长度
for (int i = 0; i < len; i++)
{
if ('a' <= str[i] && str[i] <= 'z')
{
int ind = str[i] - 'a'; // 算出偏移量
table[ind]++;
}
else if ('A' <= str[i] && str[i] <= 'Z')
{
int ind = str[i] - 'A';
table[ind + 26]++; // 前 26 个位置存储小写字母的出现次数, 后 26 个位置...大写...次数
}
}
/* 过渡模块 */
int maxn = 0;
int minn = 99999;
for (int i = 0; i < 52; i++) // 总共 52 个字母
{
if (table[i] == 0) // 这是一个小细节, 当某个单词都没有出现时, 值为 0 时, 直接跳过
continue;
if (table[i] > maxn)
maxn = table[i];
else if (table[i] < minn)
minn = table[i];
}
int num = maxn - minn;
/* 输出模块 */
if (Prime_Judge(num))
{
printf("Lucky Word\n");
printf("%d\n", num);
}
else
{
printf("No Answer\n");
printf("0\n");
}
return 0;
}
● 运行结果:
● C++ 版本:【在打表模块和 C 版本有所不同,但原理大致相同】
#include <iostream>
#include <string>
using namespace std;
/* 质数判断模块 */
int Prime_Judge(int Number)
{
if (Number == 0 || Number == 1)
return 0;
for (int i = 2; i * i <= Number; i++)
{
if (Number % i == 0) // 能被整除说明不是质数(也称为素数)
return 0;
}
return 1;
}
int a[100] = {0}; // 空间只要 >= 58 就行, 因为 'z' = 112, 'A' = 65, 112-65+1=58
int main()
{
string str;
cin >> str;
/* 打表模块 */
for (int i = 0; i < str.length(); i++)
{
int ind = str[i] - 'A';
a[ind]++;
}
/* 过渡模块 */
int maxn = 0, minn = 999999;
for (int i = 0; i < 100; i++)
{
if (a[i] == 0)
continue;
if (a[i] > maxn)
maxn = a[i];
if (a[i] < minn)
minn = a[i];
}
/* 输出模块 */
int num = maxn - minn;
if (Prime_Judge(num))
{
cout << "Lucky Word" << endl;
cout << num << endl;
}
else
{
cout << "No Answer" << endl;
cout << 0 << endl;
}
return 0;
}
六、参考附录
[1] 原题地址:https://www.lanqiao.cn/problems/527/learning/.
上一题链接: C/C++百题打卡[1/100]——扫雷 ⭐️⭐️ 考查数组.
下一题链接: C/C++百题打卡[3/100]——约瑟夫问题⭐️⭐️⭐️ 考查链表.
百题打卡总目录: 🚧 🚧 …
C/C++百日打卡[2/100]——笨小猴 ⭐️⭐️
标签: 字符串,质数筛法,2008,NOIP提高组
三日一更
2021/12/2