LeetCode #10. Regular Expression Matching プログラミング練習

問題:

Given an input string (s) and a pattern (p), 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).

Note:

s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Example 2:

Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the precedeng element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:

Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".
Example 4:

Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches "aab".
Example 5:

Input:
s = "mississippi"
p = "mis*is*p*."
Output: false

難易度: hard
入力: String
目的: 入力された文字列が入力されたパターンと一致するか判断せよ
出力: boolean


難易度hard
正規表現に関する問題です。
私はよくPythonでreライブラリを使って正規表現を使うのですが、
今回はライブラリ無しでマッチング判断をする問題ですね。
当然パターンも制限されており、a-z . *のみが入力になります。
回答方法は色々あると思います。
greedy, deep first search, dynamic programming
で答えられると思いますが、
試した結果greedyや探索では分岐条件が多く、逆に複雑になってしまいました。
なので今回はdynamic programmingを使った回答になります。

注意すべき事: 

  • *の前の文字は0でも可
  • 普通の正規表現ではなく、入力された文字列が入力されたパターンと完全に一致した場合のみtrue、部分一致は認められません。


以下回答です。

public class Solution {
    public boolean isMatch(String s, String p) {  
        boolean[] match = new boolean[s.length()+1];  
        for(int i=0; i<match.length; i++){  
            match[i] = false;  
        }  
        match[s.length()] = true;  
        for(int i=p.length()-1; i>=0; i--){  
            if(p.charAt(i)=='*'){  
                for(int j=s.length()-1; j>=0; j--){  
                    match[j] = match[j]||match[j+1]&&(p.charAt(i-1)=='.'||p.charAt(i-1)==s.charAt(j));  
                }  
                i--;  
            }else {  
                for(int j=0; j<s.length(); j++){  
                    match[j] = match[j+1]&&(p.charAt(i)=='.'||p.charAt(i)==s.charAt(j));  
                }  
                match[s.length()] = false;  
            }  
        }  
        return match[0];  
    }  
}

簡単に説明します。
まずmatch[]を全部falseに置き、matchした部分をtrueに置いていきます。
入力文字列の最後から読み取っていきます。(前から読み込むと*の処理が面倒だからです)
この問題の一番の難点は*の処理ですね。
i番目の文字が*かどうかで処理を分けます。

以外の場合:

一文字ずつマッチングします。p[i]が'.'か、s[i]と一致した場合trueに置きます。

の場合:

p[i-1]が ’.’ かs[i]と一致した場合trueとし、マッチしなくなるまで繰り返します。
ただし、この場合*の前の文字は0個でもよいので、もしmatch[j]が元々trueである場合は保持します。

ダイナミックプログラミングに関しては今度詳しく書いてみたいと思います。
googleさん達アメリカ企業の皆さんはダイナミックプログラミングが大好きでよく面接に出ます。
必ずマスターしておきたいアルゴリズムですね。

ではでは