数组和字符串
逆序输出
读入一些整数,逆序输出到一行中。已知整数不超过100个。
#include <stdio.h>
#include <string.h>
int maxn[105];//若数组较长则将其设为全局变量
int main (void)
{
int i=0,x,k;
memset(maxn,0,sizeof(maxn));//将数组清零
while((scanf("%d",&x))==1)
maxn[i++]=x;//先将x赋值给maxn[i],之后i++
for (k=i-1;k>=0;k--)
printf("%d ",maxn[k]);
return 0;
}
scanf()函数返回的是成功输入的变量的个数,当输入结束时,scanf()函数无法再次读取x,将返回0。
是按Enter/空格/TAB键吗?我们会发现,按Enter/空格/TAB键,并没有反应,这是因为程序正在等待输入。
在Windows下,输入完毕后先按Enter键,再按Ctrl+Z键,最后再按Enter键。在Linux下,输入完毕后按Ctrl+D键即可结束输入。
大数组需要定义在函数体外的原因,全局变量在静态存储区内分配内存,而局部变量是在栈内分配内存空间的。C语言编写的程序会在运行期间创建一个堆栈段,用来保存函数的调用关系和局部变量。而在main函数内部定义大数组相当于在栈内需要一个很大的空间,会造成栈的溢出。
因此,当我们需要定义一个极大的数组时,最好在main 函数外部定义这个大数组。
开灯问题
有n盏灯,编号为1~n。第1个人把所有灯打开,第2个人按下所有编号为2的倍数的开关(这些灯将被关掉),第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭)依此类推。一共有k个人,问最后有哪些灯开着?输入n和k,输出开着的灯的编号。k≤n≤1000。
:用a[1],a[2],a[3]……a[n]表示编号为1,2,3……n的灯是否开着。
#include <stdio.h>
#include <string.h>
int a[1000];
int main (void)
{
int n,k,i,j;
scanf("%d%d",&n,&k);
memset(a,0,sizeof(a));//数组清零
//0和1表示开灯和关灯两种状态
for(i=1;i<=k;i++)//人
for (j=1;j<=n;j++)//灯
if (j%i==0)
{
if (a[j]==0)
a[j]=1;
else
a[j]=0;
}
for(i=1;i<=n;i++)
if (a[i]==1)
printf("%d ",i);
printf("\n");
return 0;
}
回文字
输入一个字符串,判断它。输入字符串保证不含数字0。所谓回文串,就是反转以后和原串相同,如abba和madam。所有镜像串,就是左右镜像之后和原串相同,如2S和3AIAE。注意,并不是每个字符在镜像之后都能得到一个合法字符。在本题中,每个字符的镜像如图所示(空白项表示该字符镜像后不能得到一个合法字符)。
输入的每行包含一个字符串(保证只有上述字符。不含空白字符),判断它是否为回文
串和镜像串(共4种组合)。每组数据之后输出一个空行。
样例输入:
NOTAPALINDROME
ISAPALINILAPASI
2A3MEAS
ATOYOTA
样例输出:
NOTAPALINDROME – is not a palindrome.
ISAPALINILAPASI – is a regular palindrome.
2A3MEAS – is a mirrored string.
ATOYOTA – is a mirrored palindrome.
分析过程可以看这篇:【C语言】经典题目二
下面是代码实现:
#include<stdio.h>
#include<string.h>
int main (void)
{
char s[128]={0};
int i,j,k,n;
int flagH=1,flagJ=1;
const char a[80]="AEHIJLMOSTUVWXYZ12358";
const char b[80]="A3HILJMO2TUVWXY51SEZ8";
gets(s);
n=strlen(s);
//判断是否是回文串
for(i=0,k=n-1;i<k;i++,k--)
{
if (s[i]!=s[k])
{
flagH=0;
break;
}
}
//判断是否是镜像串
for(i=0,k=n-1;i<k;i++,j--)
{
for (j=0;s[j]!=0;j++)
{
if (s[i]==a[j])
{
if (s[k]!=b[j])
{
flagJ=0;
}
}
}
if (flagJ==0) break;
}
//根据标志量判断
if(flagH==1 && flagJ==1)
printf("%s -- is a mirrored palindrome.",s);
else if (flagH==1 &&flagJ==0)
printf(" %s-- is a regular palindrome.",s);
else if (flagH==0 && flagJ==1)
printf("%s -- is a mirrored string.",s);
else
printf("%s-- is not a palindrome.",s);
return 0;
}
习题
得分(score)
给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现
的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。
#include<stdio.h>
int main (void)
{
char a[80]={0};
int tot=0,i,k;
gets(a);
for(i=0;a[i]!='\0';i++)
{
if (a[i]=='O')
{
tot+=1;
for (k=i-1;k>=0;k--)
{
if (a[k]!='O') break;
else tot+=1;
}
}
}
printf("%d\n",tot);
return 0;
}
分子量(Molar Mass)
给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分
别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01(单位:g/mol)。例如,C6H5OH的
分子量为94.108g/mol。
#include<stdio.h>
struct Molar
{
char atom;
double mass;
};
int main (void)
{
char a[30]={0};
int i,k;
double tot=0;
gets(a);
struct Molar b[4]={'C',12.01,'H',1.008,'O',16.00,'N',14.01};
for (i=0;a[i]!='\0';i++)//遍历输入的a字符数组
{
for (k=0;k<=3;k++)
if(a[i]==b[k].atom)
{
if (a[i+1]>='1' && a[i+1]<='9')//判断字符其后一位是否为数字
tot+=b[k].mass * (a[i+1]-'0');//注意分子式中的数字都是以字符型存入字符数组的
else
tot+=b[k].mass;
}
}
printf("%lf \n",tot);
return 0;
}
数数字(Digit Counting)
把前n(n≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次(输出10个整数,分别是0,1,…,9出现的次数)。
#include<stdio.h>
#include<string.h>
char a[100000];
int main (void)
{
int k=0,i;
int b[10]={0};//用于记录1~9出现的次数
char x;//记从键盘读入的数字字符
memset(a,'\0',sizeof(a));//将字符数组清零
scanf("%s",a);
//对数组a进行遍历
for(k=0;a[k]!='\0';k++)
b[a[k]-'0']++;
//输出数组b
for(k=0;k<=9;k++)
printf("%d ",b[k]);
return 0;
}
周期串 (Periodic Strings)
如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。
输入一个长度不超过80的字符串,输出其最小周期。
#include <stdio.h>
#include <string.h>
int main (void)
{
int i,k,len,n;
int flag; //设置标志量
char s[80];
gets(s);
len=strlen(s);
for(i=1;i<=len;i++)
{
if (len%i==0) //判断是否是因子
//若是,判断能否为周期
{
flag=1;
for (k=0;k<i;k++)
{
for (n=1;n<=len/i-1;n++)
{
if (s[k]!=s[k+n*i]) flag=0;
}
}
}
if (flag==1)
{
printf("%d\n",i);
break;
}
}
return 0;
}
DNA序列(DNA Consensus String)
输入m个长度均为n的DNA序列,,到所有序列的总Hamming距离尽量
小。两个等长字符串的Hamming距离等于字符不同的位置个数,例如,ACGTGCGAHamming距离为2(左数第1, 4个字符不同)。
输入整数m和n(4≤m≤50, 4≤n≤1000),以及m个长度为n的DNA序列(只包含字母A,C,G,T),输出到m个序列的Hamming距离和最小的DNA序列和对应的距离。如有多
解,要求为字典序最小的解。例如,对于下面5个DNA序列,最优解为TAAGATAC。
TATGATAC
TAAGCTAC
AAAGATCC
TGAGATAC
TAAGATGT
如果把题目改成这样的基础版,下面是分析过程。
这里需要注意的是,题目要求如有多解,要求为字典序最小的解。所以我们可以试着从最后一个DAN序列开始循环。
#include<stdio.h>
#include<string.h>
int main(void)
{
char a[50][1000]; //用来存储DAN序列,保证长度足够长
char s[1000]; //用来存储最优DAN序列
int b[50]={0}; //记录每个DAN序列的Hamming距离值
int i=0,k,j;
int m,n;//m行n列
scanf("%d%d",&m,&n);
int min=(m-1)*n; //min一定小于等于(m-1)*n 完成初始化
for (i=0;i<m;i++)
scanf("%s",&a[i]);//输入m个DAN序列
for(k=m-1;k>=0;k--) //从最后一行开始
{
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
if (a[k][j]!=a[i][j])
b[k]++;
}
}
if(b[k]<=min)
{
min=b[k];
strcpy(s,a[k]);
}
}
//输出
//输出m个序列的Hamming值
for (j=0;j<m;j++)
printf("%d ",b[j]);
printf("\n");
//输出最小的DAN序列及对应的值
printf("%s\n%d",s,min);
return 0;
}
运行结构如下:
思考
(必要的存储量):
数组可以用来保存很多数据,但在一些情况下,并不需要把数据保存下来。下面哪些题目可以不借助数组,哪些必须借助数组?请编程实现。假设输入只能读一遍。
- 输入一些数,统计个数。
- 输入一些数,求最大值、最小值和平均数。
- 输入一些数,哪两个数最接近。
- 输入一些数,求第二大的值。
- 输入一些数,求它们的方差。
1
,输入一些数,统计个数。
显然不需要将数据存入数组中,我们只需要设置一个变量作为累加器即可。
如下:
#include <stdio.h> int main (void) {
int count=0;
double x;
while (scanf("%lf",x)==1)
count++;
printf("%d\n",count); }
2
,输入一些数,求最大值、最小值和平均数。
第二个可以利用数组实现,但也可以在循环中实现。
那么如何求平均值呢?可以定义和变量sum,每输入一个数,就将此数加到sum中,然后最后sum除以输入的数的总数即可。
#include<stdio.h>
int main (void)
{
int count=1;
double max,min,sum,x,ave;
scanf("%lf",&x);
min=max=x;
sum=x;
while(scanf("%lf",&x)==1)
{
count+=1;
if (x>=max)
{
max=x;
}
if (x<=min)
{
min=x;
}
sum+=x;
}
ave=sum/count;
printf("max=%.2lf,min=%.2lf,average=%.2lf\n",max,min,ave);
return 0;
}
3
,输入一些数,哪两个数最接近。(假设输入的都是整数)
我的想法是,,即可,将差值放到变量min中。利用循环,依次判断两个数的差值,若小于min,将将此差值 赋值给min。
#include<stdio.h>
#include<string.h> int maxn[10000];//保证数组足够大 int main (void) {
int k=0,i,x;//x用来存放每次输入的值
int count=0;//累加器
int tmp;//设置中间变量
int min;//存放差值的最小值
memset(maxn,0,sizeof(maxn));//数组初始化为0;
//输入一些数字
while(scanf("%d",&x)==1)
{
maxn[k++]=x;
count++;
}
//输入完毕,对输入的数字排序
//下采用选择法进行排序
for(i=0;i<count;i++)
{
for (k=i+1;k<count;k++)
{
if (maxn[k]<maxn[i])
//若后面的小于它,则利用中间变量交换
{
tmp=maxn[i];
maxn[i]=maxn[k];
maxn[k]=tmp;
}
}
}
min=maxn[1]-maxn[0];//初始化
for(i=1;i<count-1;i++)
{
if(maxn[i+1]-maxn[i]<min)
min=maxn[i+1]-maxn[i];
}
printf("%d\n",min);
return 0;
}
运行结果:
4.输入一些数,求第二大的值。
#include<stdio.h>
int main(void)
{
int maxF,maxS,x;
scanf("d",&x);
maxF=maxS=x;//初始化
while(scanf("%d",&x)==1)
{
if(x>maxF)
{
maxS=maxF; //易出错
maxF=x;1
}
else if(x>maxS && x<maxF)
maxS=x;
}
printf("%d\n",maxS);
return 0;
}
5.
输入一些数,求它们的方差。
#include <stdio.h>
#include <string.h>
int maxn[1000];//定义足够大的数组
int main (void)
{
memset(maxn,0,sizeof(maxn));//数组初始化为0
int x,sum=0,k=0,count=0,i;
double ave,var=0;//平均值和方差
//输入一些数
while(scanf("%d",&x)==1)
{
maxn[k++]=x;
sum+=x;
count++;
}
//计算平均值
ave=(double)sum/count;//注意强制转换sum
//计算方差
for (i=0;i<count;i++)
{
var+=(maxn[i]-ave)*(maxn[i]-ave);
}
var=var/count;
printf("%.2lf",var);
return 0;
}