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()];
    }
}



希望对大家有所帮助,欢迎大家将任何问题在回复区讨论。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值