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
【中文描述】
给两个字符串:s和p. p是正则表达式串,其中包含有三种字符:普通字符、'.'、'*'。要求实现方法,返回p是否能够匹配s.
其中:
'.'可以匹配s中任意字符。
'*'是个控制字符,在'*'前的字符在s中可以出现0或者无限次。
题目要求全部匹配,不能部分匹配。
例子:
————————————————————————————————————————————————————————————
【初始思路】
刚开始没觉得是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后的部分是否匹配的问题,这就把问题从大化小了。看下面图:
isMatchLessSpace
【描述】
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
相关文章推荐
- LeetCode Implement strStr()
- LeetCode Implement strStr()
- Ember.js 入门指南——{{action}} 助手
- Ember.js 入门指南——{{action}} 助手
- LeetCode "Move Zeroes"
- html标签认识的一些查漏补缺
- Mahout推荐的简单实现
- ajax提交数组
- 学习心得
- FMDB数据库
- 我现在对Git的认识
- office安装不了 “windows installer 服务不能更新一个或多个受保护的windows文件”
- FlushMode属性与transaction(spring注入的事务)
- 事务的特性..
- 我的感想
- IDEA 13、14 配合 gradle 时候无法正确编译资源文件的 bug
- 条款37:绝不重新定义继承而来的缺省参数值
- 分析Hibernate的事务处理机制
- 为什么hibernate需要事务?
- Unique Paths II 解答