面試經典題:「統計一個由字母與空格組成的字串中有多少個單詞?」核心考點在於有限狀態機(Finite State Machine, FSM)的建模與實作能力。單詞定義為「連續非空格字元序列」,以空格或字串結尾分隔。
定義三個狀態:
INIT:初始/剛跳過空格,準備識別新單詞IN_WORD:正在讀取單詞內部字元IN_SPACE:正在讀取分隔空格| 當前狀態 | 輸入字元 | 下一狀態 | 動作 |
|---|---|---|---|
| INIT | 非空格 | IN_WORD | 計數 +1 |
| 空格 | IN_SPACE | — | |
| IN_WORD | 非空格 | IN_WORD | — |
| 空格 | IN_SPACE | — | |
| IN_SPACE | 非空格 | IN_WORD | 計數 +1 |
| 空格 | IN_SPACE | — |
遇到 \0 直接返回計數。
#include
#include
typedef enum { INIT, IN_WORD, IN_SPACE } State;
int count_words(const char *str) {
if (!str) return 0;
State state = INIT;
int count = 0;
while (*str) {
char ch = *str++;
switch (state) {
case INIT:
if (ch != ' ') {
++count;
state = IN_WORD;
} else {
state = IN_SPACE;
}
break;
case IN_WORD:
if (ch == ' ') state = IN_SPACE;
break;
case IN_SPACE:
if (ch != ' ') {
++count;
state = IN_WORD;
}
break;
}
}
return count;
}
void test_count_words(void) {
assert(count_words(NULL) == 0);
assert(count_words("") == 0);
assert(count_words("hello") == 1);
assert(count_words("china baby hello") == 3);
assert(count_words(" leading spaces") == 2);
assert(count_words("trailing spaces ") == 2);
assert(count_words("multiple spaces") == 2);
assert(count_words("single") == 1);
}
ch == ' ' 判斷為 isspace(ch) 或查表,狀態結構完全不用動。為了進一步解耦邏輯與資料,可將轉移表提取為靜態結構體陣列:
typedef struct {
State next_state;
int inc_count; // 0 或 1
} Transition;
static const Transition table[3][2] = {
/* INPUT: 非空格=0, 空格=1 */
/* INIT */ {{IN_WORD, 1}, {IN_SPACE, 0}},
/* IN_WORD */ {{IN_WORD, 0}, {IN_SPACE, 0}},
/* IN_SPACE */ {{IN_WORD, 1}, {IN_SPACE, 0}},
};
int count_words_table(const char *str) {
if (!str) return 0;
State state = INIT;
int count = 0;
while (*str) {
int input = (*str++ == ' ') ? 1 : 0;
const Transition *tr = &table[state][input];
count += tr->inc_count;
state = tr->next_state;
}
return count;
}
此寫法將「業務規則」完全資料化,修改規則只需改表,程式碼零修改,極利於維護與單元測試。
單詞計數看似簡單,實則是「流式輸入、狀態保持、邊界處理」的縮影。掌握 FSM 建模(狀態定義 → 轉移表 → 表驅動實作)是從「寫出能跑的程式」進階到「寫出可維護、可擴展、可驗證系統」的關鍵技能。