您的位置:首页 > 其它

LeetCode (10): Regular Expression Matching [HARD]

2015-09-20 05:30 417 查看
https://leetcode.com/problems/regular-expression-matching/

【描述】

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).

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. p是正则表达式串,其中包含有三种字符:普通字符、'.'、'*'。要求实现方法,返回p是否能够匹配s.

其中:

'.'可以匹配s中任意字符。

'*'是个控制字符,在'*'前的字符在s中可以出现0或者无限次。

题目要求全部匹配,不能部分匹配。

例子:

isMatch("aa","a") → false    //解释:s中2个a,而p中一个普通字符a,显然不匹配
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true    //解释:s中2个a,而p中有'a*',根据题意,a可以出现无数次,所以是匹配的
isMatch("aa", ".*") → true    //解释:由于'.'可以代替任意字符,所以'.*'的意思就是任意字符出现任意次数,所以肯定可以匹配aa
isMatch("ab", ".*") → true    //同上,ab也能匹配
isMatch("aab", "c*a*b") → true    //解释:c在s中没有出现,但是c*是可以匹配的。然后aa匹配了a*, 最后的b互相匹配。 所以整体匹配

————————————————————————————————————————————————————————————

【初始思路】

刚开始没觉得是hard题,就觉得给2个指针,一个指s,一个指p。然后从前往后一步步比较就行了。无非就是比较当前位的时候兼顾后一位的情况,注意边界条件,仔细写应该不会出错。先写了一个,一提交wrong answer了。 用例是这样的:

s="aabbbcd", p="a*b*bbbcd"

显然,s和p是匹配的。但是用我上面的方法,就绝对匹配不成功。因为,p中的b*会直接和S中的bbb全部匹配,然后p中剩下的bbb就要和cd匹配,返回false。换句话说,我上面的算法是一条路走到黑,成功就成功,失败就失败。根本不考虑是否还有其他可能性!

【重整思路】

看到这个用例,我才反应过来,我太naive了,太没有程序猿的知觉了。事实上,仔细想想就能发现,这个题是需要回溯考虑的。可能按照s当前位和p中某'普通字符+*'模式比较是匹配的,到结尾有可能不匹配。但是如果当前位按照不与p中“字符+*”匹配(也即直接跳过p中'x*')走到最后却有可能成功。 所以,这就需要回溯考虑。如果按照既定步骤匹配到结尾不成功,我们可以回溯回来,然后从当前位用下一个策略去尝试一下。 所有的尝试里,只要成功一次,就算匹配成功!

说到回溯,程序猿的直觉告诉我,需要用递归Recursion!

【解法一:递归 Recursion】

回溯递归解法需要知道3个关键点:(1)如何确定当前步在哪里?(2)当前步有哪些决策?(3) 当前决策失败后回到哪里?

首先,如何确定当前步?由于已经考虑清楚要用递归,那么其实是不需要指针的。递归其实就是把大问题化为小问题的典型解题方案。本题的大问题是s和p是否匹配。假设s当前从左往右的一个子部分s1已经和p从左往右的一个子部分p1匹配了。那么剩下的子问题就是判断,s从s1后的部分和p从p1后的部分是否匹配的问题,这就把问题从大化小了。看下面图:

/**
* This is the O(nm) time and O(n) space DP, awesome!
* @param s
* @param p
* @return
*/
public static boolean isMatch(String s, String p) {
String[] patterns = new String[p.length()];
int i = 0, ptr = 0;
while (i != p.length()) {//parse p into tokens[], 要么单字符,要么*二元组
if (i + 1 < p.length() && p.charAt(i + 1) == '*') {
patterns[ptr++] = p.substring(i, i + 2);
i += 2;
}
else {
patterns[ptr++] = p.substring(i, i + 1);
i += 1;
}
}

boolean[] d = new boolean[s.length() + 1];
d[0] = true;
for (i = 1; i <= s.length(); ++i) d[i] = false; //d[]全部置为false
for (i = 1; i <= ptr; ++i) {
//根据tokens[], 一一判断是否和s中每个字符匹配.
String pattern = patterns[i - 1];//获取当前token
char c = pattern.charAt(0);//当前token第一个字符
if (pattern.length() == 2) {//2元组情况
for (int j = 1; j <= s.length(); ++j) {//分别针对s中字符进行匹配测试
d[j] = d[j] || (d[j - 1] && (c == '.' || c == s.charAt(j - 1)));
}
}
else {//单个情况
for (int j = s.length(); j >= 1; --j) {
d[j] = d[j - 1] && (c == '.' || c == s.charAt(j - 1));
}
}
d[0] = d[0] && pattern.length() == 2;
}
return d[s.length()];
}


isMatchLessSpace
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: