第三章 数组和字符串(例题篇)
知识点一:蛇形填数 P40
利用边界和碰撞
#include <stdio.h>
#include<string.h>
#define max 20
int set[max][max];
int main()
{
int n, x, y, tot = 0;
scanf("%d", &n);
x = -1;
y = n - 1;
memset(set, 0, sizeof(set));
while (tot < n*n)
{
while (x + 1 < n&&!set[x + 1][y])
set[++x][y] = ++tot;
while (y - 1 >= 0 && !set[x][y - 1])
set[x][--y] = ++tot;
while (x - 1 >= 0 && !set[x - 1][y])
set[--x][y] = ++tot;
while (y + 1 < n&&!set[x][y + 1])
set[x][++y] = ++tot;
}
for (int i = 0;i < n;i++)
{
for (int j = 0;j < n;j++)
{
printf("%2d ", set[i][j]);
}
printf("\n");
}
return 0;
}
知识点二:读取字符串 P43
1.scanf("%s",s)它会读入一个不含空格、TAB和回车符的字符串,存入字符数组s。
2.fgetc(fin),它读取一个打开的文件fin,读取一个字符,然后返回一个int值。如果文件结束,fgetc将返回一个特殊标记EOF,它并不是一个char。如果把fgetc(fin)的返回值强制转换为char,将无法把特殊的EOF和普通字符区分开。如果要从标准输入读取一个字符,可以用getchar,它等价于fgetc(stdin)。
3.fgetc和getchar将读取“下一个字符”,因此需要知道在各种情况下,“下一个字符”是哪个。如果用“scanf("%d",&n)”读取整数n,则要是在输入123后多加了一个空格,用getchar读取的将是这个空格;如果在“123”之后紧跟着换行,则读取到的将是回车符“\n”。
4.这里有个潜在的陷阱:不同操作系统的回车换行符是不一致的。Windows是“\r”和“\n”两个字符,Linux是“\n”,而MaxOS是“\r”。如果在Windows下读取Windows文件,fgetc和getchar会把“\r”“吃掉”,只剩下“\n”;但如果要在Linux下读取同样一个文件,他们会忠实地先读取“\r”,然后才是“\n”。如果编程时不注意,所写程序可能会在某个操作系统上是完美的,但在另一个操作系统上就错得一塌糊涂。应该避免在Linux下使用Windows格式的文件,但正如前面所强调过的:应该把自己的程序写得更鲁棒,即容错性更好。
5.fgets(buf,maxn,fin)读取完整的一行,其中buf声明为char buf[maxn]。这个函数读取不超过maxn-1个字符,然后在末尾添上结束符“\0”,因此不会出现越界的情况。之所以说可以用这个函数读取完整的一行,是因为一旦读到回车符“\n”,读取工作将会停止,而这个“\n”也会是buf字符串中最后一个有效字符(再往后就是字符串结束符“\0”了)。只有在一种情况下,buf不会以“\n”结尾:读到文件结束符,并且文件的最后一个不是以“\n”结尾。
6.fgets也有一个“标准输入版”gets。遗憾的是,gets和它的“兄弟”fgets差别比较大:其用法是gets(s),没有指明读取的最大的字符数。这里就出现了一个潜在的问题:gets将不停地往s中存储内容,而不管是否存储得下!在C11标准里,该函数已被正式删除。
7.C语言并不禁止程序读写“非法内存”。例如,声明的是char s[100],完全可以赋值s[10000]=‘a’(甚至-Wall也不会警告),但后果自负。
例题3-3 回文词和镜像词(Palindromes,UVa401) P48
输入一个字符串,判断它是否为回文串以及镜像串。输入字符串保证
不含数字0.所谓回文串,就是反转以后和原串相同,如abba和madam。
所有镜像串,就是左右镜像之后和原串相同,如2S和3AIAE。注意,并
不是每个字符在镜像之后都能得到一个合法字符。
输入的每行包含一个字符串(保证只有上述字符。不含空白字符),
判断它是否为回文串和镜像串(共4种组合)。每组数据之后输出一个空
行。
样例输入:
NOTAPALINDROME
ISAPALINILAPASI
2A3MEAS
ATOYOTA
#include <stdio.h>
#include<string.h>
#include<ctype.h>
/* 头文件ctype.h中定义的isalpha、isdigit、isprint等工具可以
用来判断字符的属性,而toupper、tolower等工具可以用来转换大小写。*/
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()
{
freopen("input.txt", "r", stdin);
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;
}
猜数字游戏的提示(Master-Mind Hints,UVa 340) P50
实现一个经典“猜数字”游戏。给定答案序列和用户猜的序列,统计有
多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对
(B)。
输入包含多组数据。每组输入第一行为序列长度n,第二行是答案序列,
接下来是若干猜测序列。猜测序列全0时该数据结束。n=0时输入结束。
样例输入:
4
1 3 5 5
1 1 2 3
4 3 3 5
6 5 5 1
6 1 3 5
1 3 5 5
0 0 0 0
10
1 2 2 2 4 5 6 6 6 9
1 2 3 4 5 6 7 8 9 1
1 1 2 2 3 3 4 4 5 5
1 2 1 3 1 5 1 6 1 9
1 2 2 5 5 5 6 6 6 7
0 0 0 0 0 0 0 0 0 0
0
#include <stdio.h>
#define maxn 1010
int main()
{
freopen("input.txt", "r", stdin);
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 (b[i] == a[i])
{
A++;
}
}
if (b[0] == 0)
break;
for (int d = 0;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)\n", A, B - A);
}
}
return 0;
}
例题3-5:生成元(Digit Generator,UVa1583) P52
如果x加上x的各个数之和得到y,就说x是y的生成元。给出
n(1<=n<=100000),求最小生成元。无解输出0。例如,n=216,121,
2005时的解分别为198,0,1979。
提示:假设所求生成元为m。不难发现m<n。为了提高效率,只需要枚举
100000内的所有正整数m,标记“m加上m的各个数字之和得到的数有一个生成元是m”,最后查表即可。
#include <stdio.h>
#include<string.h>
#define maxn 100002
int ans[maxn];
int main()
{
freopen("input.txt", "r", stdin);
int T, n;
memset(ans, 0, sizeof(ans));
for (int m = 1;m < maxn - 1;m++)
{
int x = m, y = m;
while (x > 0)
{
y += x % 10;x /= 10;
}
if (ans[y] == 0 || m < ans[y])
{
ans[y] = m;
}
}
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
printf("%d\n", ans[n]);
}
return 0;
}