8.10 多個字串與二維字元陣列(延伸知識)
有些題目一次給很多個字串,最典型的是「字元網格」:H 列 W 欄的地圖,每列是一個長度 W 的字串。存法:二維字元陣列(上冊 6.9),一列住一個字串:
char g[105][106]; // 最多 105 列;每列是一個可存 105 字元 + '\0' 的字元陣列
第二維要多開一格——g[i] 就是「第 i 列那個一維字元陣列」,它要放整列字串加上 '\0',所以第二維至少是「最大寬度 +1」(比照慣例再加點)。
¶一次讀一整列
妙的地方來了:既然 g[i] 是一個字元陣列,那 cin >> g[i] 就是 8.7 學過的「讀一個單詞」——一行指令讀進一整列:
int H, W;
cin >> H >> W;
for (int i = 0; i < H; i++) {
cin >> g[i]; // 第 i 列整串讀入,結尾自動補 '\0'
}
讀完之後,g[i][j] 就是第 i 列第 j 欄的字元(0-base),照 6.9 的巢狀迴圈遍歷即可。
¶範例程式碼
讀入 H \times W 的網格,數出寶藏 '#' 有幾個:
#include <iostream>
using namespace std;
const int MAX_H = 105;
char g[MAX_H][MAX_H + 1]; // 第二維多開一格給 '\0'
int main() {
int H, W;
cin >> H >> W;
for (int i = 0; i < H; i++) {
cin >> g[i]; // 一次讀一整列
}
int cnt = 0;
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
if (g[i][j] == '#') cnt++;
}
}
cout << cnt << '\n';
return 0;
}
執行結果(輸入 2 3 與 #.#、..#):
3
¶想用 1-base 存?
如果想讓左上角住在 g[1][1](跟題目的「第 1 列第 1 欄」對齊,上冊 6.3 的存法 A),列的方向讓 g[0] 空著就好;欄的方向有個小技巧:
for (int i = 1; i <= H; i++) {
cin >> (g[i] + 1); // 從 g[i][1] 開始放:整列往右挪一格
}
g[i] + 1 的意思是「第 i 列開頭再往後一格的位置」——把讀入的起點從 g[i][0] 挪到 g[i][1]。當然,用 6.6 的逐字讀也完全可以,還不用煩惱 '\0' 的事:
for (int i = 1; i <= H; i++) {
for (int j = 1; j <= W; j++) {
cin >> g[i][j]; // 一格一格讀,想放哪就放哪
}
}
動手試試看:把範例改成:數完 '#' 之後,再輸出每一列各有幾個 '#'(一行一個數字)。想想看你遍歷的方向跟 6.9 的「逐列展開」有什麼關係。