字符串和数组 《算法入门经典》第三章

来源:互联网 发布:手机淘宝卖话费充值 编辑:程序博客网 时间:2024/06/05 16:55

1.简单用数组存储数据

a.输出格式:%03d——整数不够三列就在前面补0

b.几种输入函数:
scanf("%s")输入字符串碰到空格或者TAB就会停下来

解决办法1:可使用fgetc(fin)可以从打开的文件fin中读取一个字符。 一般情况下应当在检查它不是EOF后再将其转换成char值。

从标准输入读取一个字符可以用getchar,它等价于fgetc(stdin)。

注意:fgetc和getchar将读取“下一个字符”,因此需要知道在各种情况下,“下一个字符”是哪个。

解决办法2:使用“fgets(buf, maxn, fin)”读取完整的一行,其中buf的声明为char buf[maxn]。 这个函数读取不超过maxn-1个字符,然后在末尾添上结束符“\0”,因此不会出现
越界的情况。一旦读到回车符“\n”,读取工作将会停止,而这个“\n”也会是buf字符串中最后一个有效字符(再往后就是字符串结束符“\0”了)。 只有在一种情况下,buf不会以“\n”结尾:读到文件结束符,并且文件的最后一个不是以“\n”结尾。 当一个字符都没有读到时,fgets返回NULL。

注意:gets已经被废除了,但为了向后兼容,仍然可以使用它。 从长远考虑,最好不要使用此函数。

eg:蛇形填数。 在n×n方阵里填入1,2,…,n×n,要求填成蛇形。

#include<stdio.h>
#include<string.h>
#define maxn 20
int a[maxn][maxn];
int main()
{
 int n,x,y,tot = 0;
 scanf("%d",&n);
 memset(a,0,sizeof(a));
 tot = a[x= 0][y = n-1] =1;
 while(tot < n*n)
 {
  while(x + 1 < n && !a[x+1][y]) a[++x][y] = ++tot;// “&&”是短路运算符, 如果x+1<n为假,将不会计算“!a[x+1][y]”
  while(y - 1 >= 0 && !a[x][y-1]) a[x][--y] = ++tot;//走迷宫时,通过数组来确定走的方向
  while(x - 1 >= 0 && !a[x-1][y]) a[--x][y] = ++tot;
  while(y + 1 < n && !a[x][y+1]) a[x][++y] = ++tot;
 }
 
 for(x = 0;x < n;x ++)
 {
  for(y = 0;y < n;y ++)
  {
   printf("%3d",a[x][y]);
  }
  printf("\n");
 }
 return 0;
}

eg:竖式问题。 找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。 输入数字集合(相邻数字之间没有格),输出所有竖式。 每个竖式前应有编号,之后应有一个空行。 最后输出解的总数。

#include<stdio.h>
#include<string.h>
int main()
{
 int count = 0;
 char s[20],buf[99];
 scanf("%s",s);
 for(int abc = 111;abc <= 999;abc ++)
 {
  for(int de = 11;de <= 99;de ++)
  {
   int x = abc * (de%10),y = abc * (de/10),z = abc * de;
   sprintf(buf,"%d%d%d%d%d",abc,de,x,y,z);//输出到字符串
   int ok = 1;
   for(int i = 0;i < strlen(buf);i ++)
   {
    if(strchr(s,buf[i]) == NULL) //返回buf[i]在s中第一次出现的位置的指针
    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;
}

eg:实现一个经典"猜数字"游戏。 给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对(B)。
直接统计可得A,为了求B(不用0-9的数组判断是否有相同数字是因为同一组也有重复的。),对于每个数字(1~9),统计二者出现的次数c1和c2,则min(c1,c2)就是该数字对B的贡献。 最后要减去A的部分。

#include<stdio.h>
#define maxn 1010
int main()
{
 int n,a[maxn],b[maxn];
 int kase = 0;
 while(scanf("%d",&n) == 1 && n) 
 {
  printf("Game%d:\n",++kase);
  for(int i = 0;i < n;i ++)
  scanf("%d",&a[i]);
  for(;;)
  {
   int A = 0,B = 0;
   for(int i = 0;i < n;i ++)
   {
    scanf("%d",&b[i]);
   if(a[i] == b[i])
   A ++;
   }
   
  
  if(b[0] == 0)break;
  for(int d = 1;d <= 9;d ++)
  {
   int c1 = 0,c2 = 0;
   for(int i = 0;i < n;i ++)
   {
    if(a[i] == d) c1 ++;
    if(b[i] == d) c2 ++;
   }
   if(c1 < c2) B += c1;
   else
   B += c2;
  }
  printf("    (%d,%d)",A,B-A);
  }
 }
 return 0;
}

2.用数组自己做表格

a.已知完整的、数据之间关系没有规律的一组数,实现用数组记录:

eg:把手放在键盘上时,稍不注意就会往右错一位。 这样,输入Q会变成输入W,输入J会变成输入K等。

#include<stdio.h>
char s[] = "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
int main() {
int i, c;
while((c = getchar()) != EOF) {
for (i=1; s[i] && s[i]!=c; i++); //找错位之后的字符在常量表中的位置
if (s[i]) putchar(s[i-1]); //如果找到,则输出它的前一个字符
else putchar(c);
}
return 0;
}

eg:回文串和镜像串(事先把各个字母、数字的回文,镜像字符准备好,以便后来查找。

#include<stdio.h>
#include<string.h>
#include<ctype.h>
const char *rev = "A   3  HIL JM O   2TUVWXY51SE Z  8 ";
const char *msg[] = {"not a palindrome","a regular palindrome","a mirrored string","a mirrored palindrome"};
char r(char ch)
{
 if(isalpha(ch))
 return rev[ch-'A'];
 else
 return rev[ch-'0'+25];
}
int main()
{
 char s[30];
 while(scanf("%s",s) == 1)
 {
  int len = strlen(s);
  int p = 1,m = 1;
  for(int i = 0;i < (len + 1) / 2;i ++)
  {
   if(s[i] != s[len - 1- i])
   p = 0;
   if(r(s[i]) != s[len - 1- i])
   m = 0;
  }
  
  printf("%s -- is %s.\n\n",s,msg[m*2+p]);
 }
 
 return 0;
}

eg:如果x加上x的各个数字之和得到y,就说x是y的生成元。 给出n(1≤n≤100000),求最小生成元。 无解输出0。如:198 = 198+1+ 9 + 8 = 216
一次性枚举100000内的所有正整数m,标记“m加上m的各个数字之和得到的数有一个生成元是m”,最后查表即可。

#include<stdio.h>
#include<string.h>
#define maxn 100005
int ans[maxn];
int main()
{
 int T,n;
 memset(ans,0,sizeof(ans));
 for(int m = 1;m < maxn;m ++)  //m是n的生成元,数组按n排序
 {
  int x = m,y = m;
  while(x > 0)
  {
   y += x % 10;
   x /= 10;
  }
  if(ans[y] == 0 || m < ans[y])//m是n的最小生成元
  ans[y] = m;
 }
 
 scanf("%d",&T);
 while(T--)
 {
  scanf("%d",&n);
  printf("%d\n",ans[n]);
 }
 return 0;
}

3.用循环数组寻找特定排序(使用指针可以不用新建数组)

eg:输入一个长度为n(n≤100)的环状DNA串(只包含A、 C、 G、 T这4种字符)的一种表示法,你的任务是输出该环状串的最小表示。 例如,CTCC的最小表示是
CCCT,CGAGTCAGCT的最小表示为AGCTCGAGTC(字典序: 对于两个字符串,从第一个字符开始比较,当某一个位置的字符不同时,该位置字符较小的串,字典序较小(例如,abc比bcd小);如果其中一个字符串已经没有更多字符,但另一个字符串还没结束,则较短的字符串的字典序较小(例如,hi比history小)。

#include<stdio.h>
#include<string.h>
#define maxn 105
int less(const char *s,int p,int q)
{
 int n = strlen(s);
 for(int i = 0;i < n;i ++)
 if(s[(p+i)%n] != s[(q+i)%n])
 return s[(p+i)%n] < s[(q+i)%n];
 
 return 0;
}
int main()
{
 int T;
 char s[maxn];
 scanf("%d",&T);
 while(T--)
 {
  scanf("%s",s);
  int ans = 0;
  int n = strlen(s);
  for(int i = 1;i < n;i ++)
  if(less(s,i,ans))
  ans = i;
  
  for(int i = 0;i < n;i ++)
  putchar(s[(i+ans)%n]);
  putchar('\n');
 }
 
 return 0;
}

eg:找星星(每颗星星有6条边,假设每条边的长度量化为[1,100w],现在有N(1<N<10w)颗星星,请判断是否存在相同的星星。

相同的条件为:6条边的长度依次相等(包括顺时针相等和逆时针相等)。)

bool is_same_clockwise(hashtable *p,int k)
{
 for(int j = 0;j < 6;j ++)
 {
  bool flag = true;
  for(int i = 0;i < 6;i ++)
   if(star[k].length[i] != p->length[(i+j)%6])
   {
    flag = false;
    break;
   }
   if(flag)
    return true;
 }
 return false;
}
bool is_same_counter_clockwise(hashtable *p,int k)
{
 for(int j = 0;j < 6;j ++)
 {
  bool flag = true;
  for(int i = 0;i < 6;i ++)
   if(star[k].length[i] != p->length[(11-i-j)%6])
   {
    flag = false;
    break;
   }
   if(flag)
    return true;
 }
 return false;
}

0 0