[LeetCode][Facebook面试题] 通配符匹配和正则表达式匹配,题 Wildcard Matching
开篇
通常的匹配分为两类,一种是正则表达式匹配,pattern包含一些关键字,比如'*'的用法是紧跟在pattern的某个字符后,表示这个字符可以出现任意多次(包括0次)。
另一种是通配符匹配,我们在操作系统里搜索文件的时候,用的就是这种匹配。比如 "*.pdf",'*'在这里就不再代表次数,而是通配符,可以匹配任意长度的任意字符组成的串。所以"*.pdf"表示寻找所有的pdf文件。
在算法题中,往往也会有类似的模拟匹配题,当然考虑到当场实现的时间,会减少通配符数量或者正则表达式关键字的数量,只留那么几个,即便如此,这类题目也是属于比较难的题目了==。
正则表达式匹配
例题如下:
Regular Expression Matching
http://basicalgos.blogspot.com/2012/03/10-regular-expression-matching.html
'.' 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
这道题是面Facebook时遇到的一道题。
要处理的关键字有两个'*', '.' ,第二个比较好办,第一个比较麻烦,
因为'*'可以表示任意数量,因此当*(p+1) == '*'时,我们可以掠过'*'之前的字符,直接++p,或者如果*s == *(p-1)或*(p-1) == '.',我们可以跳过任意个这样的s。因此,'*'的处理被跳过多少个s划分成了多个子问题,我用递归函数来处理这些子问题。当时的代码还没有这么简洁,这是我修改后的代码:
bool isMatch(char *s, char *p){
if(*s == '\0' && *p == '\0')
return true;
if (*(p+) == '*'){
while(*p == *s || *p == '.'){ //若*s和*p相等,挨个略过
if(isMatch(s++, p+));
return true;
}
return isMatch(s, p+); //若*s和*p不等,直接略过*p;或者当*(p+2) == '\0'时的最后处理
}
if(*s == *p || *p == '.')
return *s == '\0' ? false : isMatch(s+, p+);
return false;
}
通配符匹配
我们以LeetCode上的一题为例。
Wildcard Matching
Implement wildcard pattern matching with support for '?' and '*'.
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence). 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", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
required function:
bool isMatch(const char *s, const char *p)
通配符有两个:"?"和"*"
因为*是可以匹配任意字符串的,因此还是划分子问题,我一开始的思路是遇到*后,和上一题一样使用递归来处理子问题。
代码:
class Solution {
public:
bool isMatch(const char *s, const char *p) {
if(*s == '\0'){
if(*p == '\0') return true;
if(*p != '*') return false;
}
if(*p == '?') return isMatch(++s, ++p);
else if(*p == '*'){
while(*(++p) == '*');
for(; *s != '\0'; ++s){
if(isMatch(s, p)) return true;
}
return isMatch(s, p);
}else{
if(*p == *s) return isMatch(++s, ++p);
return false;
}
return false;
}
};
但是这样做超时。
为了节约时间,我用空间换时间,用rec[][]记录了比较结果。
class Solution {
public:
bool isMatch(const char *s, const char *p) {
int lens = , lenp = ;
const char *s1 = s, *p1 = p;
for(; *s1 != '\0'; ++s1, ++lens);
for(; *p1 != '\0'; ++p1, ++lenp);
if(lenp == ) return false;
if(lens == ) return true;
rec = new int*[lens+];
for(int i = ; i <= lens; ++i){
rec[i] = new int[lenp+];
for(int j = ; j <= lenp; ++j){
rec[i][j] = -;
}
}
return isMatchCore(s, s, p, p);
}
private:
int** rec;
bool isMatchCore(const char *oris, const char *s, const char *orip, const char *p) {
if(*s == '\0'){
if(*p == '\0') return true;
if(*p != '*') return false;
}
if(rec[s-oris][p-orip] >= ) return rec[s-oris][p-orip];
if(*p == '?') return isMatchCore(oris, ++s, orip, ++p);
else if(*p == '*'){
while(*(++p) == '*');
for(; *s != '\0'; ++s){
if(isMatchCore(oris, s, orip, p)) return true;
}
return isMatchCore(oris, s, orip, p);
}else{
if(*p == *s) return isMatchCore(oris, ++s, orip, ++p);
return false;
}
return false;
}
};
结果依然超时。
原因在于即便使用了带记录的递归,对于p上的每一个'*',依然需要考虑'*' 匹配之后字符的所有情况,比如p = "c*ab*c",s = "cddabbac"时,遇到第一个'*',我们需要用递归处理p的剩余部分"ab*c" 和s的剩余部分"ddabbac"的所有尾部子集匹配。也就是:"ab*c"和"ddabbac","ab*c" 和"dabbac"的匹配,"ab*c" 和"abbac"的匹配,... ,"ab*c" 和"c"的匹配,"ab*c" 和"\0"的匹配。
遇到第二个'*',依然如此。每一个'*'都意味着p的剩余部分要和s的剩余部分的所有尾子集匹配一遍。
然而,我们如果仔细想想,实际上,当p中'*'的数量大于1个时,我们并不需要像上面一样匹配所有尾子集。
依然以 p = "c*ab*c",s = "cddabbac"为例。
对于p = "c*ab*c",我们可以猜想出它可以匹配的s应该长成这样: "c....ab.....c",省略号表示0到任意多的字符。我们发现主要就是p的中间那个"ab"比较麻烦,一定要s中的'ab'来匹配,因此只要s中间存在一个"ab",那么一切都可以交给后面的'*'了。
所以说,当我们挨个比较p和s上的字符时,当我们遇到p的第一个'*',我们实际只需要不断地在s的剩余部分找和'ab'匹配的部分。
换言之,我们可以记录下遇到*时p和s的位置,记为presp和press,然后挨个继续比较*(++p)和*(++s);如果发现*p != *s,就回溯回去,p = presp,s = press+1, ++press;直到比较到末尾,或者遇到了下一个'*',如果遇到了下一个'*',说明 "ab"部分搞定了,下面的就交给第二个'*'了;如果p和s都到末尾了,那么就返回true;如果到末尾了既没遇到新的'*',又还存在不匹配的值,press也已经到末尾了,那么就返回false了。
这样的思路和上面的递归比起来,最大的区别就在于:
遇到'*',我们只考虑遇到下一个'*'前的子问题,而不是考虑一直到末尾的子问题。从而避免大量的子问题计算。
我们通过记录 presp和press,每次回溯的方法,避免使用递归。
代码:
class Solution {
public:
bool isMatch(const char *s, const char *p) {
const char *presp = NULL, *press = NULL; //previous starting comparison place after * in s and p.
bool startFound = false;
while(*s != '\0'){
if(*p == '?'){++s; ++p;}
else if(*p == '*'){
presp = ++p;
press = s;
startFound = true;
}else{
if(*p == *s){
++p;
++s;
}else if(startFound){
p = presp;
s = (++press);
}else return false;
}
}
while(*p == '*') ++p;
return *p == '\0';
}
};
[LeetCode][Facebook面试题] 通配符匹配和正则表达式匹配,题 Wildcard Matching的更多相关文章
- WildcardMatching和Regex,通配符匹配和正则表达式匹配
WildcardMatching:通配符匹配 算法分析: 1. 二个指针i, j分别指向字符串.匹配公式. 2. 如果匹配,直接2个指针一起前进. 3. 如果匹配公式是*,在字符串中依次匹配即可. 注 ...
- leetcode 第43题 Wildcard Matching
题目:(这题好难.题目意思类似于第十题,只是这里的*就是可以匹配任意长度串,也就是第十题的‘.*’)'?' Matches any single character. '*' Matches any ...
- Leetcode 10. 正则表达式匹配 - 题解
版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C#版 - L ...
- [LeetCode] Regular Expression Matching 正则表达式匹配
Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...
- LeetCode(10):正则表达式匹配
Hard! 题目描述: 给定一个字符串 (s) 和一个字符模式 (p).实现支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符. '*' 匹配零个或多个前面的元素. 匹配应该覆盖整 ...
- 剑指offer——面试题19:正则表达式匹配
#include"iostream" using namespace std; bool MatchCore(char*str,char* pattern); bool Match ...
- leetcode 10 Regular Expression Matching(简单正则表达式匹配)
最近代码写的少了,而leetcode一直想做一个python,c/c++解题报告的专题,c/c++一直是我非常喜欢的,c语言编程练习的重要性体现在linux内核编程以及一些大公司算法上机的要求,pyt ...
- Leetcode(10)正则表达式匹配
Leetcode(10)正则表达式匹配 [题目表述]: 给定一个字符串 (s) 和一个字符模式 (p).实现支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符. '*' 匹配零个或 ...
- [LeetCode] 10. Regular Expression Matching 正则表达式匹配
Given an input string (s) and a pattern (p), implement regular expression matching with support for ...
随机推荐
- URAL 1519 Formula 1(插头DP,入门题)
Description Background Regardless of the fact, that Vologda could not get rights to hold the Winter ...
- 【转】C++后台开发之我见
工作也快两年了,偶然看到自己以前写过的一些技术博客,发现自己自毕业后一直没有更新过自己的技术博客,趁现在是刚过完春节快要回公司工作之际,谈谈我个人对后台开发的一些个人见解,希望能够对在校的学生或者刚刚 ...
- “Hello World!”团队——Alpha发布用户使用报告
博客内容: 1.用户体验报告表 2.用户评论截图 3.总结 一.用户体验报告表 用户使用报告 用户序号 用户姓名(化名) 性别 用户职业 使用频次 用户评论 1 小董 女 文学在读硕士 5 1.游戏界 ...
- "Hello world!"团队第八次会议
Scrum会议 今天是我们"Hello world!"团队第八次召开会议,博客内容是: 1.会议时间 2.会议成员 3.会议地点 4.会议内容 5.todo list 6.会议照片 ...
- scanf格式控制符的完整格式
scanf格式控制的完整格式: % * m l或h 格式字符 ①格式字符与printf函数中的使用方式相同,以%d.%o.%x.%c.%s.%f.%e,无%u格式.%g ...
- wwnjld团队第二轮迭代成员分数
2014-01-05 第二轮迭代团队内成员分数如下(依据分数分配规则以及团队会议协商所得结果): 吴渊渊 23 汪仁贵 21.5 高小洲 19.5 聂建 22.5 吕家辉 23.5 程志 10
- 福大软工1816:beta版本冲刺前准备
BETA 版冲刺前准备 队名:第三视角 作业链接 组长博客 应作业要求为了更加顺利地开展beta版本的冲刺,上次的alpha版本展示后,我们组对之前开发过程中存在的各种问题进行了全面的讨论,并对其进行 ...
- 一个demo让你彻底理解Android中触摸事件的分发
注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...
- LintCode-71.二叉树的锯齿形层次遍历
二叉树的锯齿形层次遍历 给出一棵二叉树,返回其节点值的锯齿形层次遍历(先从左往右,下一层再从右往左,层与层之间交替进行) 样例 给出一棵二叉树 {3,9,20,#,#,15,7}, 返回其锯齿形的层次 ...
- linux上使用J-Link调试S3C2440裸机代码
linux上使用J-Link调试S3C2440裸机代码 工具: segger的jlink仿真器 segger的jlink for linux 交叉编译工具链里面的arm-xx-linux-xx-gdb ...