例題3-1 TeX中的引號(Tex Quotes, UVa 272)
在TeX中,左雙引號是“``”,右雙引號是“’’”。輸入一篇包含雙引號的文章,你的任務是 把它轉換成TeX的格式。
樣例輸入:
“To be or not to be,” quoth the Bard, “that
is the question”.
樣例輸出:
``To be or not to be,’’ quoth the Bard, ``that
is the question’’.
編程提示
- 本題的關鍵是,如何判斷一個雙引號是左雙引號還是右雙引號。方法很簡單:使用一個標誌變量即可。
- 使用fgetc(fin)可以從打開的文件fin中讀取一個字符。一般情況下應當在檢 查它不是EOF後再將其轉換成char值。從標準輸入讀取一個字符可以用getchar,它等價於 fgetc(stdin)。
- "fgets(buf, maxn, fin)"將讀取完整的一行放在字符數組buf中。應當保證 buf足夠存放下文件的一行內容。除了在文件結束前沒有遇到“\n”這種特殊情況外,buf總是 以“\n”結尾。當一個字符都沒有讀到時,fgets返回NULL。
程序3-5 TeX中的引號
#include<stdio.h>
int main()
{
int c, q = 1;
while((c = getchar()) != EOF)
{
if(c == '"')
{
printf("%s", q ? "``" : "''");
q = !q;
}
else printf("%c", c);
}
return 0;
}
例題3-2 WERTYU(WERTYU, UVa10082)
把手放在鍵盤上時,稍不注意就會往右錯一 位。這樣,輸入Q會變成輸入W,輸入J會變成輸 入K等。
輸入一個錯位後敲出的字符串(所有字母均 大寫),輸出打字員本來想打出的句子。輸入保 證合法,即一定是錯位之後的字符串。例如輸入中不會出現大寫字母A。
樣例輸入:
O S, GOMR YPFSU/
樣例輸出:
I AM FINE TODAY.
編程提示
- ,每輸入一個字符,都可以直接輸出一個字符,因此getchar是輸入的理 想方法。
- :善用常量數組往往能簡化代碼。定義常量數組時無須指明大小,編譯器會 計算。
- :如何進行這樣輸入輸出變換呢?一種方法是使用if語句或者switch語 句,如"if(c == ‘W’) putchar(‘Q’)"。但很明顯,這樣做太麻煩。一個較好的方法是使用常量數 組,下面是完整程序:
程序3-6 WERTYU
#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;
}
例題3-3 迴文詞(Palindromes, UVa401)
輸入一個字符串,判斷它是否爲迴文串以及鏡像串。輸入字符串保證不含數字0。所謂 迴文串,就是反轉以後和原串相同,如abba和madam。所有鏡像串,就是左右鏡像之後和原
串相同,如2S和3AIAE。注意,並不是每個字符在鏡像之後都能得到一個合法字符。在本題 中,每個字符的鏡像如圖3-3所示(空白項表示該字符鏡像後不能得到一個合法字符)。
輸入的每行包含一個字符串(保證只有上述字符。不含空白字符),判斷它是否爲迴文 串和鏡像串(共4種組合)。每組數據之後輸出一個空行。
提示
- 既然不包含空白字符,可以安全地使用scanf進行輸入。迴文串和鏡像串的判斷都不復雜,並且可以一起完成,詳見下面的代碼。使用常量數組,只用少量代碼即可解決這個看上去有些複雜的題目。
- :頭文件ctype.h中定義的isalpha、isdigit、isprint等工具可以用來判斷字符 的屬性,而toupper、tolower等工具可以用來轉換大小寫。如果ch是大寫字母,則ch-'A’就是 它在字母表中的序號(A的序號是0,B的序號是1,依此類推);類似地,如果ch是數字, 則ch-'0’就是這個數字的數值本身。
程序3-7 迴文詞
#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'];
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;
}
例題3-4 猜數字遊戲的提示(Master-Mind Hints, UVa 340)
實現一個經典"猜數字"遊戲。給定答案序列和用戶猜的序列,統計有多少數字位置正確 (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
樣例輸出:
Game 1:
(1,1)
(2,0)
(1,2)
(1,2)
(4,0)
Game 2:
(2,4)
(3,2)
(5,0)
(7,0)
提示
- 直接統計可得A,爲了求B,對於每個數字(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) { // n=0時輸入結束
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; //正常的猜測序列不會有0,所以只判斷第一個數是否爲0即可
for(int d = 1; d <= 9; d++) {
int c1 = 0, c2 = 0; // 統計數字d在答案序列he猜測序列中各出現多少次
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, ACM/ICPC Seoul 2005, UVa1583)
如果x加上x的各個數字之和得到y,就說x是y的生成元。給出n(1≤n≤100000),求最小 生成元。無解輸出0,例如,n=216,121,2005時的解分別爲198,0,1979。
提示
- 只需一次性枚舉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++) {
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;
}
例題3-6 環狀序列(Circular Sequence, ACM/ICPC Seoul 2004, UVa1584)
長度爲n的環狀串有n種表示法,分別爲從某 個位置開始順時針得到。例如,圖3-4的環狀串 有10種表示:
CGAGTCAGCT,GAGTCAGCTC,AGTCAGCTCG等。在這些表示法中,字典序最小的稱 爲"最小表示"。
輸入一個長度爲n(n≤100)的環狀DNA串(只包含A、C、G、T這4種字符)的一種表 示法,你的任務是輸出該環狀串的最小表示。例如,CTCC的最小表示是 CCCT,CGAGTCAGCT的最小表示爲AGCTCGAGTC。
提示
- 。所謂字典序,就是字符串在字典中的順序。一般地, 對於兩個字符串,從第一個字符開始比較,當某一個位置的字符不同時,該位置字符較小的 串,字典序較小(例如,abc比bcd小);如果其中一個字符串已經沒有更多字符,但另一個 字符串還沒結束,則較短的字符串的字典序較小(例如,hi比history小)。字典序的概念可 以推廣到任意序列,例如,序列1, 2, 4, 7比1, 2, 5小。
#include<stdio.h>
#include<string.h>
#define maxn 105
// 環狀串s的表示法p是否比表示法q的字典序小?
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];//返回值有true false的,如果滿足小於,說明這個位置的取值更優。
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;
}
黃沙百戰穿金甲,不破樓蘭終不還。