題目
題目鏈接
劍指offer:正則表達式匹配
題目描述
請實現一個函數用來匹配包括'.'和'*'的正則表達式。模式中的字符.表示任意一個字符,而*表示它前面的字符可以出現任意次(包含0次)。 在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配,但是與"aa.a"和"ab*a"均不匹配。
解題思路
1.分析題目
- 輸入:一個待匹配字符串,一個待匹配正則表達式
- 輸出:字符串與正則表示式相匹配則輸出
true,否則輸出false - 條件:正則表達式中僅含兩個特殊字符;
.表示任意一個字符,*表示它前面的字符能夠出現0~無數次。題目未說明是否貪心匹配(即盡可能多的匹配)的情況下,應默認非貪心匹配。即應考慮類似aaa與a*aa相匹配的情況。同時注意可能會出現.*這類能夠匹配所有字符的表達式。
2.可能出現的情況
考慮字符串和正則相匹配時能夠相消去,則當最終兩者為空時為匹配成功;中途有不匹配且不帶*的字符或是最終不能相互消去時為匹配失敗。這種情況很顯然使用指針移動特別好用。
考慮情況如下:
- 字符串空時,正則式不為空。后者剩余字符若皆為帶/*字符,則應繼續消去。
- 當前指針指向字符能夠消去時(正則式當前指針字符與字符串當前指針字符相同,或是正則式當前指針字符為
.且字符當前指針不為\0),需要考慮正則指針的下一步指向是否為*。為*則需要考慮此時是否非貪心匹配。 - 當前指針指向字符不可相互消去時,需考慮正則指針下一步指向是否為
*,為*則可忽略正則指針此時的不匹配字符,令其向前移兩位;否則直接匹配失敗。例如aaa與ab*c*aa能匹配成功。
3.思考解題步驟
在2中所考慮的情況都能夠用起初的思路--指針移動解決,故確定以指針為解題方向。
同時,每一步對於當前指針指向字符的處理過程都是相似的,故考慮使用遞歸使代碼更加簡潔。
很慚愧,在編寫2中指針字符相互消去的情況時,我發現自己寫的代碼總是會遺漏部分情況,查看錯誤樣例后發現對*的非貪心匹配處理要寫出大量邏輯繁瑣且容易出錯的代碼。在查看討論區的解答后,發現自己一直走了死胡同。對於這種問題應考慮使用動態規划的思想,將情況一分為二的處理,分別計算繼續判斷當前*與結束當前*匹配的情況。
具體代碼
class Solution {
public:
bool match(char* str, char* pattern)
{
if (*str == '\0' && *pattern == '\0')
return true;
// 1.匹配帶*正則(包括非貪心情況)
if (*str == *pattern || (*pattern == '.' && *str != '\0'))
{
// 不是正則式
if (*(pattern + 1) != '*')
return match(++str, ++pattern);
// 是正則式
// 將*字符的匹配一分為二的處理
return match(str, pattern + 2) || match(str + 1, pattern);
}
// 消去無用帶*字符
else if (*(pattern + 1) == '*')
{
pattern += 2;
return match(str, pattern);
}
return false;
}
};
