Wildcard Matching (Recursive and Non Recursive method) (Leetcode 網易有道面試題)

題目描述

 (http://www.leetcode.com/onlinejudge 倒數第三題)

Implement wildcard pattern matching with support for '?' and '*'.

'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false

這個題目的非遞歸算法,被網易有道面試官,面到過。並且要求寫出程序,其實非遞歸算法要考慮的東西還是很多的,如果沒有見過算法,真是很難寫出來。筆者在這裏給出遞歸和非遞歸算法。僅供參考


題目分析: 

和正則表達式的匹配有點不同的是,在這裏*號是不依賴與以前的符號是,是一個通配符。*是一個多重通配符,而*是一個單一通配符。


遞歸解法

其實題目很容易寫出一個遞歸的方法:

遞歸方法首先檢查輸入兩個串是否都是空,如果是空,認爲已經匹配完畢,直接返回true。

如果被匹配串是空,模式串如果是 *, 那麼是true

如果模式串是空,那麼結果是false

class Solution {
public:
   
    bool isMatch(const char *s, const char *p) {
	
		if(*s == 0 && *p == 0){ //兩個串是否都是空,如果是空,認爲已經匹配完畢,直接返回true。
			return true;
		}
		if(*s == 0){ //如果被匹配串是空,模式串如果 全是*,那麼就是true
			while(*p == '*') p++;
			return !(*p);
		}
		if(*p == 0){
			return false;
		}

		int i;
		for( i = 0; s[i] && p[i] ; i++){ //匹配過程

			if(s[i] != p[i]){
				if(p[i] == '?'){ //單一通配符
					continue;
				}
				else if(p[i] == '*'){ //多重通配符
					s += i;
					p += i;
					return isMatch(s, p + 1) || isMatch(s + 1, p);//匹配和不匹配,兩種情況
				}
				else{
					return false;
				}
			}
		}
		return isMatch(s + i, p + i); //匹配剩餘的
	}
};


非遞歸方法

非遞歸方法的主要思想是,從匹配串和模式串開頭匹配,如果發現當前不能匹配了,則檢測之前是否出現過 *, 如果有*出現過,則模式串跳過當前不匹配的字符,模式串跳過星號。
例如

abbc  a*c***

第一次 startLoop
i = 0;  a 和 a 匹配成功
i = 1;  b 和 * 發現不能匹配,程序流到 case '*', 因爲 * 和 b 可以匹配,所以將 p 和 s 各自 + i ,標記 star爲真, 重新開始匹配。

第二次 startLoop
i = 0;  b 和 c 不能匹配 跳到 starcheck 看看 之前是不是有 *, 如果存在 *,則 s + 1, p 不增加(此時p還是 上一個 * 的下一個位置), 然後繼續匹配

第三次 startLoop:
i = 0; c = c 可以匹配 
這時候 s[i] == 0
跳出循環
{
while(p[i] == '*')i++;
		return !(p[i]);
}
用來檢測當 s 串匹配完了以後 p串是不是全爲 * ,如果全是*,則可以匹配,否則匹配失敗。

	bool isMatch(const char *s, const char *p) {

		int i;
		bool star = false;

startLoop:
		{
			for( i = 0; s[i] ; i++){ 
				switch(p[i])
				{ 
				case '?': 
					break;
				case '*': //如果當前 爲*, 那麼匹配上剛剛不能匹配的那個字符,並且將p移動到 * 結束後的 第一個字符
					star = true;  //p 每次指向的位置,要麼是最開始,要麼是 * 結束的第一個位置
					p += i;  
					s += i;
					while(*p == '*'){++p;} //檢測p是不是後面全是 *
					if(!(*p)) return true;

					goto startLoop; //重新開始循環, p 指向 * 結束後的 第一個 非*
				default:
					if(s[i] != p[i]){ //如果 s[i] != p[i]
						goto starCheck;
					}
				}
			}
		}
		while(p[i] == '*')i++;
		return !(p[i]);

starCheck:
		{
			if(star){ //當 s[i] != p[i], 檢測 p 之前是不是 *, 如果是 *,那麼我們將s 向後移動一個,其實就是枚舉 s 到 s[i],和 p[0] 匹配。
				s++; 
				goto startLoop;
			}
			return false;
		}      
	}

算法是速度是相當快的,在第一種情況下TLE的情況下,算法二僅僅需要 80ms,看來非遞歸的效率確實是很高的。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章