2-21作业题解

第一题:统计分数,示例代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int marks[101],counts[101];
int main()
{
int a,n=0;
while(scanf("%d",&a) != EOF)
{
n++;
marks[n] = a;
counts[marks[n]]++;
}
int max = 0,c;
for(int i = 0;i<=100;i++)
if(counts[i] > max)
max = counts[i];

for(int i = 0;i<=100;i++)
if(counts[i] == max)
cout<<i<<" ";

return 0;
}

第二题:统计单词的平均长度:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define MAXLENGTH 189819
char word[MAXLENGTH];
int main()
{
int lengthSum = 0, count = 0;
while(scanf("%s",word) != EOF){
count++;
lengthSum+=strlen(word);
}
if(count == 0)
printf("没有单词输入");
else printf("输入的单词的平均长度为:%f",lengthSum / (count * 1.0));

return 0;
}
3.3 最长回文子串
例3-4 回文串。
输入一个字符串,求出其中最长的回文子串。子串的含义是:在原串中连续出现的字符串片段。回文的含义是:正着看和倒着看相同。如abba和yyxyy。在判断时,应该忽略所有标点符号和空格,且忽略大小写,但输出应保持原样(在回文串的首部和尾部不要输出多余字符)。输入字符串长度不超过5000,且占据单独的一行。应该输出最长的回文串,如果有多个,输出起始位置最靠左的。
样例输入:Confuciuss say:Madam,I'm Adam.
样例输出:Madam,I'm Adam
【分析】
由于输入的字符比较复杂,首先,不能用scanf("%s")输入字符串,可用下述两种方法解决下列问题:
第1种方法是使用fgetc(fin),它读取一个打开的文件fin,读取一个字符,然后返回一个int值。因为如果文件结束,fgetc将返回一个特殊标记EOF,它并不是一个char。如果要从标准输入读取一个字符,可以用getchar(),它等价于fgetc(stdin)。
提示3-14:使用fgetc(fin)可以打开的文件fin中读取一个字符。一般情况下应当在检查它不是EOF后再将其转换成char值。从标准输入读取一个字符可以用getchar(),它等价于fgetc(stdin)。
fgetc()和getchar()将读取“下一个字符”,如果是空格,会正常读取;若是换行,读取到的将是回车符'\n'。
潜在的陷阱:不同操作系统的回车换行符是不一致的。Windows是'\r'和'\n'两个字符,Linux是'\n',而MacOS是'\r'。如果在Windows下读到Windows文件,fgetc()和getchar()会把'\r'吃掉,只剩下'\n';但如要要在Linux下读取同样一个文件,它们会先读到'\r',然后才是'\n'。这个问题在竞赛时一定要注意。
提示3-15:在使用fgetc和getchar时,应该避免写出和操作系统相关的程序。
第2种方法是使用fgets(buf,MAXN,fin)读了完整的一行,其中buf的声明为char buf[MAXN]。这个函数读取不超过MAXN-1个字符,然后在末尾上结束符'\0',因此不会出现越界的情况。之所以说可以用这个函数读取完整的一行,是因为一旦读到回车符'\n',读取工作将会停止,而这个'\n'也会是buf字符串中最后一个有效字符(再往后就是字符串的结束符'\0'了)。只有一种情况下,buf不会以'\n'结尾:读到文件结束符,并且文件的最后一个不是以'\n'结尾。
提示3-16:fgets(buf,MAXN,fin)将读取完整的一行放在字符数组buf中。你应当保证buf足够存放下文件的一行内容。除了在文件结束符前没有遇到'\n'这种特殊情况外,buf总是以'\n'结尾。当一个字符都没有读到时,fgets返回NULL。
gets(s)表示从标准输入设备读取字符串存入s所指向的数组中,成功时返回指针s,否则返回NULL。但是gets(s)没有指明读到的最大字符数,gets函数也不管s的可用空间有多大。
提示3-17:C语言并不禁止程序读写“非法内存”。例如你声明的是char s[100],你完全可以赋值s[10000]='a'(甚至-Wall也不会警告),但后果自负。
提示3-18:C语言中的gets(s)存在缓冲区溢出漏洞,不推荐使用。
选择fgets函数可以解决“输入有空格”的问题,它可以一次性读取一行,最为方便。
接下来,解决“判断时忽略标点,输出进却要按原样”的问题,可以用一个通用的方案:预处理。构造一个新字符串,不包含原来的标点符号,而且所有字符变成大写(顺便解决了大小写的问题):
n = strlen(buf);
m=0;
for(i = 0; i < n; i++)
if(isalpha(buf[i])) s[m++] = toupper(buf[i]);
说明:isalpha(c)的原型包含在ctype.h中,它用于判断字符c是否为字母(大写或小写)。用toupper(c)返回c的大写形式。这样处理之后,buf保存的就是原串的所有字母了。
提示3-19:当任务比较复杂时,可以用预处理的方式简化输入,并提供更多的数据供使用复杂的字符串处理题目往往可以通过合理的预处理简化任务,便于调试。
提示3-20:头文件ctype.h中定义的isalpha、isdigit、isprint等工具可以用来判断字符的属性,而toupper、tolower等工具可以用来转换大小写。
说明:(1)isalpha(c)用来检查c是否为字母,如果是字母,则返回1;否则返回0。
(2)isdigit(c)用来检查c是否为数字(0~9),如果是数字,则返回1;否则返回0。
(3)isprint(c)用来检查c是否为可打印字符(不包括空格),其ASCII码值在0x21~0x7e之间,如果是可打印字符,则返回1;否则返回0。
(4)toupper(c)用来将c字符转换为大写字母,返回c对应的大写字母。
(5)tolower(c)用来将c字符转换为小写字母,返回c对应的小写字母。
下面来枚举回文串的起点和终点,然后判断它是否真的是回文串。
int max=0;
for(i = 0; i < m; i++)
for(j = i; j < m; j++)
if(s[i..j]是回文串 && j-i+1 > max) max = j-i+1;
“当前最大值”变量max,它保存的是目前为止发现的最长回文子串的长度。如果串s的第i个字符到第j个字符(记为s[i..j])是回文串,则检查长度j-i+1是否超过max。
最后,判断s[i..j]是否为回文串的方法如下:
int ok = 1;
for(k = i; k <= j; k++)
if(s[k] != s[i+j-k]) ok = 0;
s[k]的“对称”位置是s[i+j-k],因为只要一次比较失败,就应把标记变量ok置为0。
完整的程序如下:
程序3-5 最长回文子串(1)
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXN 5000 + 10
char buf[MAXN], s[MAXN];
int main(){
int n, m = 0, max = 0;
int i, j, k;
fgets(buf, sizeof(s), stdin);
n = strlen(buf);
for(i = 0; i < n; i++)
if(isalpha(buf[i])) s[m++] = toupper(buf[i]);
for(i = 0; i < m; i++)
for(j = i; j < m; j++) {
int ok = 1;
for(k = i; k <= j; k++)
if(s[k] != s[i+j-k]) ok = 0;
if(ok && j-i+1 > max) max = j-i+1;
}
printf("max = %d\n", max);
return 0;
}
在实际编程时,经常先编写一个具备主要功能的程序,再加以完善。甚至可以先写一个只有输入输出功能的“骨架”,但是要确保它正确。这样,每次只添加一点点功能,而且写一点就测试一点,和一次写整个程序相比,更加不容易出错。这种方法称为迭代式开发。
提示3-21:在程序比较复杂时,除了在设计阶段可以用伪代码理清思路外,编码阶段可以采用迭代时开发——每次只实现一点小功能,但要充分测试,确保它工作正常。
程序3-5能顺利求出样例数据中最长回文串的长度。现在接下来的任务是输出这个回文串:要求原样输出,并且尽量靠左。由于是从左到右枚举的,所以现在只剩下唯一的问题:原样输出。
由于在求max值时,不知道s[i]和s[j]在原串buf中的位置。因此,必须增加一个数组p,用p[i]保存s[i]在buf中的位置。在预处理得到,然后在更新max的同时把p[i]和p[j]保存到x和y,最后输出buf[x]到buf[y]中的所有字符。
但是上面的方法发现速度很慢,所以换一种方式:枚举回文串的“中间”位置i,然后不断往外扩展,直到有字符不同。提示:长度为奇数和偶数的处理方式是不一样的。
完整的程序如下:
程序3-6 最长回文子串(2)
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define MAXN 5000 + 10
char buf[MAXN], s[MAXN];
int p[MAXN];
int main(){
int n, m = 0, max = 0, x, y;
int i, j;
fgets(buf, sizeof(s), stdin);
n = strlen(buf);
for(i = 0; i < n; i++)
if(isalpha(buf[i])) {
p[m] = i;
s[m++] = toupper(buf[i]);
}
for(i = 0; i < m; i++) {
for(j = 0; i-j >= 0 && i+j < m; j++) {
if(s[i-j] != s[i+j]) break;
if(j*2+1 > max) { max = j*2+1; x = p[i-j]; y = p[i+j]; }
}
for(j = 0; i-j >= 0 && i-j+1 < m; j++) {
if(s[i-j] != s[i-j+1]) break;
if(j*2+2 > max) { max = j*2+2; x = p[i-j]; y = p[i+j+1]; }
}
}
for(i = x; i <= y; i++)
printf("%c", buf[i]);
printf("\n");
return 0;
}
例3-3 竖式问题。
找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前应有编号,之后应有一个空行。最后输出解的总数。具体格式见样例输出(为了便于观察,竖式中的空格改用小数点显示,但你的程序应该输出空格,而非小数点)。
样例输入:2357
样例输出:
<1>
..775
x..33
-----
.2325
2325.
-----
25575
The number of solutions=1
【分析】
本题的解题策略是尝试所有的abc和de,判断是否满足条件。写出如下的伪代码:
char s[20];
int count = 0;
scanf("%s", s);
for(abc = 111; abc <= 999; abc++)
for(de = 11; de <= 99; de++)
if(“abc*de”是个合法的竖式) {
printf("<%d>\n", ++count);
打印abc*de的竖式和其后的空行
count++;
}
printf("The number of solutions = %d\n", count);
说明:(1)char s[20]是一个定义字符数组的语句,scanf("%s",s)表示从键盘输入一个字符串给字符数组s。
(2)char是“字符型”的意思,而字符是一种特殊的整数。每一个字符都有一个整数编码,称为ASCII码。C语言中允许用直接的方法表示字符,还有以反斜线开头的字符(转义序列,Escape Sequence)。
(3)在stdlib.h中有一个函数atoi,它的函数原型如下:
int atoi(char *s)
它表示将字符串s中的内容转换成一个整型数返回,如字符串“1234”,则函数返回值是1234。
(4)在stdlib.h中有一个函数itoa,它的函数原型如下:
char *itoa(int value,char *string,int radix)
它表示将整数value转换成字符串存入string, radix为转换时所用基数(保存到字符串中的数据的进制基数 2 8 10 16),返回指向转换后的字符串的指针 。
例如,itoa(32,string,10)是将32变成十进制数一个字符串“32”,并返回指向这个字符串的指针;itoa(32,string,16)是将32变成十进制数一个字符串“20”,并返回指向这个字符串的指针。
提示3-7:C语言中的字符型用char表示,它实际存储的是字符的ASCII码。字符常量可以用单引号法表示。在语法上可以把字符当作int型使用。
语句scanf("%s", s);表示读入一个不含空格、TAB和回车符的字符串,存入字符数组s中,s前面没有&符号。
提示3-8:在scanf("%s",s)中,不要在s前面加上&符号。如果是字符数组char s[MAXN] [MAXL],可以用scanf("%s",s[i])读取第i个字符串。
接下来有两个问题:判断和输出。先考虑输出。首先计算第一行乘积x=abc*e,然后是第二行y=abc*d,最后是总乘积z=abc*de,然后一次性打印出来:
printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n",abc,de,x,y,z);
完整程序如下:
程序3-4 竖式问题
#include<stdio.h>
#include<string.h>
int main(){
int i, ok, abc, de, x, y, z, count = 0;
char s[20], buf[99];
scanf("%s", s);
for(abc = 111; abc <= 999; abc++)
for(de = 11; de <= 99; de++) {
x = abc*(de%10); y = abc*(de/10); z = abc*de;
sprintf(buf, "%d%d%d%d%d", abc, de, x, y, z);
ok = 1;
for(i = 0; i < strlen(buf); i++)
if(strchr(s, buf[i]) == NULL) ok = 0;
if(ok) {
printf("<%d>\n", ++count);
printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n",abc,de,x,y,z);
}
}
printf("The number of solutions = %d\n", count);
return 0;
}
说明:(1)sprintf函数
sprintf是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数格式化字符串上。
此函数的功能是把格式化的数据写入某个字符串,它的返回值是字符串长度。包含此函数的头文件是stdio.h。
例如,本程序中的sprintf(buf,"%d%d%d%d%d",abc,de,x,y,z);语句的功能是将整数abcdexyx打印成字符串存储在串buff中。
(2)strchr函数
strchr函数定义如下:
char *strchr(const char *s,char c);
此函数的功能是查找字符串s中首次出现字符c的位置。它的返回值是返回首次出现c的位置的指针,如果s中不存在c则返回NULL。包含此函数的头文件是string.h。
例如,本程序的if语句中strchr(s, buf[i])的功能是查找字符串s中首次出字符buf[i]的位置。如果strchr(s, buf[i])==NULL,则表明字符串s中没有buf[i]的字符。
(3)sprintf函数、printf函数、fprintf函数的区别
printf输出到屏幕,fprintf输出到文件,而sprintf输出到字符串。需要注意是应该保证写入的字符串有足够的空间。
提示3-9:可以用sprintf把信息输出到字符串,用法和printf、fprintf类似。但你应当保证字符串足够大,可以容纳输出信息。
字符串的空间应为字符个数加1,这是因为C语言的字符串是以空字符'\0'结尾的。
函数strlen(s)的作用是获取字符串s的实际长度,即函数strlen(s)返回的是结束标记之前的字符个数。因此这个字符串中的各个字符依次是s[0],s[1],…,s[strlen(s)-1],而s[strlen(s)]正是结束标记'\0'。
提示3-10:C语言中的字符串是'\0'结尾的字符数组,可以用strlen(s)返回字符串s中结束标记之前的字符个数。字符串中的各个字符是:s[0],s[1],…,s[strlen(s)-1]。
提示3-11:由于字符串的本质是数组,它也不是“一等公民”,只能用strcpy(a,b)、strcmp(a,b)、strcat(a,b)来执行“赋值”、“比较”和“连接”操作。而不能用=、==、<=、+等运算符。上述函数都在string.h中声明。
除了字符串之外,还要注意++count和count++的用法。++count本身的值是加1以后的,但count++的值是加1之前的(原来的值)。
注意:滥用++count和count++会带来很多隐蔽的错误,所以最好的方法是避开它们。
提示3-12:滥用++、--、+=等可以修改变量值的运算符很容易带来隐蔽的错误。建议每条语句最多只用一次这种运算符,并且它所修改的变量在整条语句中只出现一次。
提示3-13:但编译选项-Wall编译程序时,会给出很多(但不是所有)警告信息,以帮助程序员查错。但并不能解决所有的问题:有些“错误”程序是合法的,只是这些动作不是你所期望的。

猜你喜欢

转载自www.cnblogs.com/longxue1991/p/10425939.html