基礎算法題-正則相關

題一

Implement regular expression matching with support for ‘.’ and ‘*’.

‘.’ 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:
boolean isMatch(String s, String 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

不考慮轉義等特殊情況的匹配。
正則的常見實現方式有三種:DFA、Backtracking、NFA。
這裏簡單的匹配,可以用以下兩種方式,第一種通過遞歸的方式,第二種通過動態規劃。

遞歸

首先看遞歸,這裏主要共考慮三種情況。
1. 首字符爲’*’,直接判定不合法。例如”*sdf”,”s**df”。
2. 第二個字符不爲’*’,此時需要判斷第一個字符是否可以匹配匹配串的第一個字符,相等或者’.’。相等則匹配串和模式串都截去首字符,遞歸處理。
3. 第二個字符爲’*’,當模式串和匹配串的首字符匹配,此時需要進行兩種處理,第一種認爲模式串首字符出現0次,跳過模式串前兩個字符,進行再匹配。第二種認爲模式串首字符出現了多次,再將匹配串截取,再匹配,直至匹配成功或者失敗。當模式串和匹配串首字符不匹配,跳過模式串前兩個字符,進行再匹配。

遞歸解決代碼如下:

boolean isMatch(String s, String p) {
    if (p.isEmpty())
        return s.isEmpty();
    if (p.startsWith("*"))
        return false;
    if (p.length() == 1 || p.charAt(1) != '*') {
        if (s.isEmpty() || (p.charAt(0) != '.' && p.charAt(0) != s.charAt(0))) {
            return false;
        } else {
            return isMatch(s.substring(1), p.substring(1));
        }
    }
    while (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')) {
        if (isMatch(s, p.substring(2))) {//出現0次
            return true;
        }
        s = s.substring(1);
    }
    //出現0次
    return isMatch(s, p.substring(2));
}
動態規劃

動態規劃,一般解題思路:

  1. 刻畫一個最優解的結構特徵。
  2. 遞歸的定義最優解的的值。
  3. 計算最優解的值,通常採用自底向上方法。
  4. 利用計算出的信息構造一個最優解。

按照以上步驟來解決這道題。
尋找最優子結構
找到最優子結構,然後利用這個子結構從子問題的最優解構造出原問題的最優解。

設匹配串S=s_1,s_2,...,s_m ,模式串P=p_1,p_2,...,p_n ,最終是否匹配成功記爲isMatch,可看爲布爾型。
1. 如果p_n ’*’,若s_mp_n 匹配,則isMatch爲S_m1P_n1 匹配的結果。
2. 如果p_n =’*’,若p_n1s_m 匹配,則isMatch爲S_mP_n1S_mP_n2S_m1P_n 匹配結果進行或處理後的值。若不匹配,則isMatch爲S_mP_n2 匹配的結果。

遞歸解
用一個布爾類型的二維數組來建立最優解的遞歸式,通過上述,可以寫成如下:

dp[i,j]=true dp[i1,j1] (dp[i,j1]||dp[i,j2]||dp[i1,j]) (dp[i,j2])i=0 & j=0i, j>0, p_n'\*',s_ip_ji, j>0, p_j='\*',s_ip_j1i, j>0, p_j='\*',s_ip_j1

計算
通過自底向上地計算。匹配問題共有m*n個子問題。
結果
直接獲取二維數組最後的值,即爲匹配的結果。
部分過程模擬示意圖如下:


regex

動態規劃解決代碼如下:

boolean isMatchByDP(String s, String p) {
    if (p.isEmpty()) {
        return s.isEmpty();
    }
    int len_s = s.length(), len_p = p.length();
    //申請,默認爲false
    boolean[][] dp = new boolean[len_s + 1][len_p + 1];
    dp[0][0] = true;
    //處理特殊情況,s爲空的情況,例如 ""和".*"
    for (int i = 0; i < len_p; i++) {
        if (p.charAt(i) == '*' && dp[0][i - 1]) {
            dp[0][i + 1] = true;
        }
    }
    for (int i = 0; i < len_s; i++) {
        for (int j = 0; j < len_p; j++) {
            if (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.')
                dp[i + 1][j + 1] = dp[i][j];
            else if (p.charAt(j) == '*') {
                if (s.charAt(i) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {
                    //出現一次,0次,出現多次
                    dp[i + 1][j + 1] = (dp[i + 1][j] || dp[i + 1][j - 1] || dp[i][j + 1]);
                } else {
                    //出現0次
                    dp[i + 1][j + 1] = dp[i + 1][j - 1];
                }
            }
        }
    }
    return dp[len_s][len_p];
}

題二

阿里的筆試題,由題一簡單變換下。’?’匹配每個字符,’*’匹配任意串。簡單修改部分代碼即可。

遞歸

遞歸解決如下:

boolean isMatch(String s, String p) {
    if (p.isEmpty()) {
        return s.isEmpty();
    }
    if (p.charAt(0) != '*') {
        if (s.isEmpty() || (p.charAt(0) != s.charAt(0) && p.charAt(0) != '?')) {
            return false;
        } else {
            return isMatch(s.substring(1), p.substring(1));
        }
    }
    while (!s.isEmpty() && (p.charAt(0) == '*')) {
        if (isMatch(s, p.substring(1))) {
            return true;
        }
        s = s.substring(1);
    }
    return isMatch(s, p.substring(1));
}
動態規劃

動態規劃解決如下:

boolean isMatch_dp(String p, String p) {
    if (p.isEmpty()) {
        return p.isEmpty();
    }
    boolean[][] dp = new boolean[p.length() + 1][p.length() + 1];
    dp[0][0] = true;
    for (int i = 0; i < p.length(); i++) {
        if (p.charAt(i) == '*' && dp[0][i])
            dp[0][i + 1] = true;
    }
   for (int i = 0; i < p.length(); i++) {
        for (int j = 0; j < p.length(); j++) {
            if (p.charAt(i) == p.charAt(j) || p.charAt(j) == '?') {
                dp[i + 1][j + 1] = dp[i][j];
            } else {
                if (p.charAt(j) == '*') {
                    //多次,一次
                    dp[i + 1][j + 1] = dp[i][j + 1] || dp[i][j];
                }
            }
        }
    }
    return dp[p.length()][p.length()];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章