LeetCode:正則表達式匹配

補上週缺失的LeetCode;

題目:

給你一個字符串 s 和一個字符規律 p,請你來實現一個支持 ‘.’ 和 ‘*’ 的正則表達式匹配。

‘.’ 匹配任意單個字符
‘*’ 匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字符串 s的,而不是部分字符串。

說明:

s 可能爲空,且只包含從 a-z 的小寫字母。
p 可能爲空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。

來源:力扣(LeetCode)

解題思路:

way一:回溯法

用Pattern和s進行匹配時,Pattern有幾種情況:
cond1:pattern中沒“*”號,則pattern[i] in {text[i],"."},則說明匹配;
cond2:pattern中有“*”號:
在cond2中,pattern和s的模式匹配形式有如下幾種:
first = pattern[i] in {text[i],"."} and i<len(text)
if i+1 <= len(pattern)-1 and pattern[i+1] == “*”:
return isMatch(s[i],pattern[i+2]) or (first and isMatch(s[i+1],pattern[i])
else:
return isMatch(s[i+1],pattern[i+1])

上述的幾種情況,有以下幾種遞歸形式:dp(i,j) :
dp(i+1,j) #1
dp(i,j+2) #2
dp(i+1,j+1) #3
可以看出,如果我們現在想去dp(i+2,j+2),則可通過以下幾種路徑來實現:
dp(i,j) -> #3 -> #3 -> dp(i+2,j+2)
dp(i,j) -> #1 -> #1 -> #2 -> dp(i+2,j+2)
由此可知,在上述遞歸算法中,存在着 無數條 重疊子問題,因此,我們可以用 “動態規劃”的方法對其進行優化。
這裏 所謂“動態規劃”,其實就是在“遞歸”的基礎上,加了一個“備忘錄”,來記住 各個子問題的 value;
先給出 “遞歸算法” 的code,在 “way二”中,給出“動態規劃”的code;

//Java
class Solution{
    public boolean isMatch(String s,String p){
        if(pattern.isEmpty()):return s.isEmpty();
        first = (!s.isEmpty() && (p.charAt(0) == s.charAt(0) || p.charAt(0) == "."));
        if(p.length()>=2 && p.charAt(1)=="*"){
            return isMatch(s.substring(0),p.substring(2)) || (first && isMatch(s.substring(1),p))
        else:
            return (first && isMatch(s.substring(1),p.substring(1)))
   }
}    

way二:動態規劃法

動態規劃法 是在 遞歸算法的基礎上,加了一個備忘錄,記錄已經處理過的子問題,從而 防止程序進行重複計算。其code如下:

//java
class Solution{
    public boolean isMatch(String s,String p){
        boolean[][] dp = new boolean[s.length()+1][p.length()+1];  //這裏之所以用 p.length+1,是爲了防止 p.substring(j+2)時溢出;   
        return dp(0,0,s,p);
    }
    public boolean dp(int i,int j,String s,String p){
        boolean ans;
        if(dp[i][j] != null){
            return dp[i][j];
        } //使用備忘錄中記錄的值,避免 子問題的重複計算;
        if(j == p.length()){
            ans = i == s.length();
        } // i,j都超出了 數組index,這種情況發生在P中有 星號 的時候
        else{
            first = (i < s.length() && (p.charAt(j) == s.charAt(i) || p.charAt(j) == "."));
            if(j<= p.length()-2 && p.charAt(j+1) == "*"){
                ans = (dp(i,j+2,s,p) || (first && dp(i+1,j,s,p)));
            } //要麼 *代表0次,要麼*代表1次
            else{
               ans = (first && dp(i+1,j+1,s,p));
            }
        }
        dp[i][j] = ans;
        return ans;
    }
}   

總結:
我覺得最讚的一點是,巧妙的利用dp(i+1,j,s,p)的方式來實現 * 的無限循環;


看過一部電影,聽過一句話:
“這世上壞人之所以猖獗,是因爲好人的無所作爲” ;
喜歡 唐晶,胸懷正念,也能比賤鬥恨;

今天推薦一首歌《聽風無涯》:

是誰信筆描繪水墨江山
蒼青色依舊深沉如海
往事終於繚亂成繁華斑斕
望不穿他眼中流年荏苒
輕聲在夢中
喚無涯成滄海
像楓色飄零
轉眼情波成潭
漸漸消失漸漸消失
將回憶點燃
留下殘骸不在看

在也回不去的年少時光
在也不想見的人
後悔遇見

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