文本串 `str`,模式串 `data`(長度 `len`)。
char* my_strstr(const char* str, const char* data) {
if (NULL == str || NULL == data) return NULL;
int len = strlen(data);
if (0 == len) return (char*)str; // 空模式串匹配開頭
while (*str && strlen(str) >= (size_t)len) { // ⚠️ 每次迴圈呼叫 strlen(str)!
int i;
for (i = 0; i < len; ++i) {
if (str[i] != data[i]) break;
}
if (i == len) return (char*)str; // 找到
++str;
}
return NULL;
}
`while` 條件中呼叫 `strlen(str)` 導致每次外層迴圈掃描剩餘字串長度。若文本長度 M 遠大於模式長度 N,總操作約 O(M²/2),極度低效。
寫入 `check_length_of_str(str, len)`:只檢查 `str[0..len-1]` 是否含 `\0`,O(N) 替代 O(M)。
int check_length_of_str(const char* str, int len) {
for (int i = 0; i < len; ++i)
if ('\0' == str[i]) return 0;
return 1;
}
char* my_strstr_v2(const char* str, const char* data) {
if (NULL == str || NULL == data) return NULL;
int len = strlen(data);
if (0 == len) return (char*)str;
while (*str && check_length_of_str(str, len)) { // O(N) 檢查
int i;
for (i = 0; i < len; ++i)
if (str[i] != data[i]) break;
if (i == len) return (char*)str;
++str;
}
return NULL;
}
觀察:外層迴圈每次 `str++`。若上一輪 `str[0..len-1]` 合法(無 `\0`),則本輪只需檢查 `str[len]` 是否為 `\0` 即可!
char* my_strstr_v3(const char* str, const char* data) {
if (NULL == str || NULL == data) return NULL;
int len = strlen(data);
if (0 == len) return (char*)str;
// 進入迴圈前,先檢查一次完整長度
if (strlen(str) < (size_t)len) return NULL;
while (*str) {
int i;
for (i = 0; i < len; ++i)
if (str[i] != data[i]) break;
if (i == len) return (char*)str; // 找到
// 關鍵優化:只檢查新進窗口的最後一字元
if ('\0' == str[len]) break; // 剩餘長度不足
++str;
}
return NULL;
}
void test_strstr() {
assert(NULL == my_strstr(NULL, "china"));
assert(NULL == my_strstr("hello world", "china"));
assert(NULL != my_strstr("hello china", "china"));
assert(NULL != my_strstr("china", "china")); // 開頭匹配
assert(NULL == my_strstr("abc", "abcd")); // 模式比文本長
printf("All tests passed.\n");
}
暴力法經優化後,對於短模式串、隨機文本已具實用效能。但最壞情況(如 `str="aaaaa...aab"`, `data="aaab"`)仍為 O(M×N)。下一篇將深入 **KMP 演算法**,從根本上消除回溯,達成 O(M+N) 理論下界。