leetcode 10 Regular Expression Matching(Dynamic Programming)

題意:

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

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", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

設左邊字符串爲s,右邊字符串爲p。判斷是否可以用右邊的字符串完全匹配左邊的字符串。

其中‘.’可以代表任何一個單個字符,‘*’代表其之前的字符有任意個(例如"c*"可以代表 沒有c,1個c,2個c...)

倒數第二個樣例解釋: “.*”用來代表“..”,“..”可以完全匹配“ab”

倒數第一個樣例解釋:“c*a*b”中“c*”代表空字符(0個c),“a*”代表“aa”


解決方法:

1.暴力深搜

剛開始我們需要解決的問題是:

判斷s和p從各自的起始位置s[0] p[0]開始,直到他們的結束位置s[s.length()-1],p[p.length()-1]爲止是否能完全匹配。

假設現在已經匹配到了s[i],p[j](二者之前的子串完全匹配),現在問題轉化成:

判斷s和p從各自的起始位置s[i] p[j]開始,直到他們的結束位置s[s.length()-1],p[p.length()-1]爲止是否能完全匹配。

以上是狀態轉化。


由於p中字符"*" "."的存在,要考慮到的問題多了一些:

(1)當前狀態起始位置爲s[i] p[j],p[j+1] != '*':若s[i] == p[j], 狀態轉移到起始位置爲s[i+1] p[j+1];若s[i] != p[j],返回false。

(2)當前狀態起始位置爲s[i] p[j],p[j+1] == '*':

因爲*的特殊功能,所以p[j]這個字符,可以當作空字符(不存在),這樣狀態就轉換爲:狀態起始位置爲s[i] p[j+2]

    可以當作1個字符:若s[i] == p[j], 狀態轉移到起始位置爲s[i+1] p[j+2];

    可以當作2個字符:若s[i] == p[j]且s[i+1] == p[j], 狀態轉移到起始位置爲s[i+2] p[j+2];

    可以當作3個字符:若s[i] == p[j]且s[i+1] == p[j]且s[i+2] == p[j], 狀態轉移到起始位置爲s[i+3] p[j+2];

    。。。。。。

代碼:

class Solution {
public:
    bool dfs(string s, string p, int i, int j){
        if(j==p.length())
            return i==s.length();
        //p[j+1] != '*';
        if(j==p.length()-1 || p[j+1]!='*')
        {
            if(i==s.length()|| s[i]!=p[j] && p[j]!='.')
                return false;
            else
                return dfs(s,p,i+1,j+1);
        }
        //p[j+1]=='*'
        while(i<s.length() && (p[j]=='.' || s[i]==p[j]))
        {
            if(dfs(s,p,i,j+2))
                return true;
            i++;
        }
        return dfs(s,p,i,j+2);
    }
public:
    bool isMatch(string s, string p) {
        return dfs(s, p, 0, 0);
    }
};


這個方法的運行時間爲150-190ms;加上狀態記錄數組,我認爲時間複雜度變成了O(n^2),但是運行時間(82ms)減少的幅度不太明顯(相比動態規劃做法來說)。我不是很明白爲什麼時間減少不太明顯,關於這個問題,非常感謝大家能提供一些意見和建議。以下是加上狀態記錄的代碼:


class Solution {
    bool vis[1005][1005];
    bool res[1005][1005];
public:
    bool dfs(string s, string p, int i, int j){
        if(vis[i][j]) return res[i][j];
        vis[i][j] = true;
        if(j==p.length())
            return res[i][j] = (i==s.length());
        if(j==p.length()-1 || p[j+1]!='*')
        {
            if(i==s.length()|| s[i]!=p[j] && p[j]!='.'){
                return res[i][j] = false;
            }
            else{
                return res[i][j] = dfs(s,p,i+1,j+1);
            }
        }
        //p.charAt(j+1)=='*'
        while(i<s.length() && (p[j]=='.' || s[i]==p[j]))
        {
            if(dfs(s,p,i,j+2)){
                return res[i][j] = true;
            }
            i++;
        }
        return res[i][j] = dfs(s,p,i,j+2);
    }
public:
    bool isMatch(string s, string p) {
        //memset(vis, false, sizeof(vis));
        return dfs(s, p, 0, 0);
    }
};



2.動態規劃

/**
         * f[i][j]: if s[0..i-1] matches p[0..j-1]
         * if p[j - 1] != '*'
         *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
         * if p[j - 1] == '*', denote p[j - 2] with x
         *      f[i][j] is true iff any of the following is true
         *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
         *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
         * '.' matches any single character
         */

這是我看到的一個思路,感覺特別好。

f[i][j]狀態定義爲s的前i 個字符 與 p的前j個字符是否完全匹配:true/false

(1)p[j-1] != '*'時,直接根據f[i-1][j-1]和s[i-1] p[j-1]轉移狀態

if p[j - 1] != '*'
      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]

(2)p[j-1] == '*'時,

p[j-2]可以當作空字符,此時f[i][j]狀態由f[i][j-2]狀態直接得到;

p[j-1]可以當作>=1個字符此時若s[i-1] == p[j-2]只需要f[i-1][j]爲true,s[i-1]這個字符就可以用‘*’重複出來的p[j-2]來匹配(而不用管之前這個p[j-1]已經重複造出來過幾個p[j-2])


代碼:爲了加深自己的理解,自己用java寫了一下。

public class Solution {
    public static boolean isMatch(String s, String p) {
        boolean[][] f = new boolean[s.length()+1][p.length()+1];

        f[0][0] = true;
        boolean tmp = true;
        for(int j = 1; j <= p.length(); j++){
            if(tmp && p.charAt(j-1) != '*' && j < p.length() && p.charAt(j) != '*') tmp = false;
            if(tmp && j == p.length() && p.charAt(j-1) != '*') tmp = false; 
            f[0][j] = tmp;
        }
        for(int i = 1; i <= s.length(); i++){
            for(int j = 1; j <= p.length(); j++){
                if(p.charAt(j-1) != '*'){
                    f[i][j] = f[i-1][j-1] && (s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '.');
                }
                else{
                    f[i][j] = f[i][j-2] || ((s.charAt(i-1) == p.charAt(j-2) || p.charAt(j-2) == '.') && f[i-1][j]);
                }
            }
        }
        //System.out.println(f[1][2]);
        return f[s.length()][p.length()];
    }
}



希望對大家有所幫助,歡迎大家將任何問題在回覆區討論。



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