Leetcode——44Wildcard Matching && 10 Regular Expression Matchi
来源:互联网 发布:网络维修电脑招聘 编辑:程序博客网 时间:2024/05/16 05:38
本周做的两道题有点类似,一道是通配符匹配问题,一道是正则表达式匹配问题。难度有点大,前者没有用DP或者递归,后者用了DP来实现。
通配符匹配
问题描述
- ‘?’可以匹配任意单一字符;
- ‘ * ’可以匹配任意的字符串(包括空串)
isMatch("aa","a") → falseisMatch("aa","aa") → trueisMatch("aaa","aa") → falseisMatch("aa", "*") → trueisMatch("aa", "a*") → trueisMatch("ab", "?*") → trueisMatch("aab", "c*a*b") → false
思路
一开始想到的是采用贪心的思想,即尽可能多的匹配两个字符串,分为三种情况:(使用变量i表示字符串s的第i个字符,变量j表示字符串p的第j个字符,初始为0)
1. 当s[i]和p[j]相等,或者p[j]是‘?’时,说明匹配成功;
2. 当p[j]不是‘*’,也不是‘?’且s[i]与p[j]不相等时,直接返回错误;
3. 当p[j]是‘*’时,匹配字符串s[i]以及后面的字符,直到p[j+1]与s的第[i+k]个字符相等。
举个例子:s = abeedc p = ab*c,一开始匹配了a和b,当p[2]是‘*’时,s[2] 与p[3]不相同,所以继续比较s[3]....直到得到s[6]是与p[j+1]相等的,这时候说明p中的‘*’代表了s中的‘eed’子字符串。
看上去这种做法是可以的,然后提交之后,结果是WA,问题是:当s = abcgrdrrede p = ab*de,此时:
如果用上面的解法,p中‘*’代表了s中的“cgr”,后面匹配一个‘d’之后就无法继续匹配了,因此得到的是false;
然而,明显可以看出,如果p中的‘*’代表了子串“cgrdrre”,那么可以匹配的,结果应该是true。
所以,这种方法不适合于上面这种情况。需要采用回溯的方法。
如果记下上一次‘*’出现时i和j的位置,然后当有不匹配出现时,就回到‘*’去执行匹配,每次匹配一个字符后继续往后遍历,一旦发现还是不同,就回到‘*’出现的位置继续下一个匹配,所以‘*’所在的位置要记录,同时,出现‘*’时候的s所在位置也要记录。以s = abcgrdrrede,p = ab*de作为例子:
1)循环遍历s和p,首先是匹配了‘a’和‘b’,当i = 2,j = 2时,p[j]出现‘*’,此时记录出现‘*’的位置ipos = 2,jpos = 2,同时,i要减1,因为此时的s[i]没有和p[j]匹配上,要从j后面的字符找;
2)继续遍历s,i = 2,j = 3,发现不匹配,那么,i回到ipos(2)的位置,j回到jpos(2)的位置,表示通配符‘*’现在代表了s[2]字符,ipos要加1(特别记住)
3)此时i = 3,j = 3,再次比较s[3]和p[3]依旧不匹配,重复以上过程。
4)当i = 5,j = 3时,匹配了字符s[5]的‘d’;此时ipos是4,jpos还是2;
5)接下来i = 6,j = 4,发现不匹配了,就可以回到ipos和jpos的位置,同样重新开始匹配;
6)i = 5,j = 3,又匹配了‘d’,但此时ipos是5,jpos是2;
7)接下来i = 6,j = 4,发现不匹配了,就可以回到ipos和jpos的位置,同样重新开始匹配;
6)i = 6,j = 3,接下来就是按照上面的过程继续进行了。
算法描述:
1.遍历s和p时,当遇到‘*’时,采用两个标志符ipos和jpos记录当前位置;
2.当遇到不匹配的情况时,回溯到‘*’所在的位置,将‘*’代替当前s中不能被p匹配的一个字符,然后继续遍历,直到s遍历结束。
代码
bool isMatch(string s, string p) {//首先,如果p中没有任何通配符,那直接比较两个字符串即可if (p.find('?') == p.npos && p.find('*') == p.npos) {if (p == s)return true;elsereturn false;}//ipos和jpos用于记录上一个"*"出现的位置,用于回溯int i = 0, j = 0, ipos = -1, jpos = -1;for (i = 0; i < s.length(); i++, j++) {if (p[j] == '*') {ipos = i;jpos = j;i--;}else {//当发生不匹配时,考虑上一个“*”的位置if (p[j] != '?' && p[j] != s[i]) {if (ipos >= 0) {i = ipos++;j = jpos;}elsereturn false;}}}while (p[j] == '*')j ++;if (j == p.length())return true;elsereturn false;}
正则表达式匹配
问题描述
实现正则表达式匹配,其中出现的非通用字符有‘.’和‘ * ’- ‘.’可以匹配任意单一字符;
- ‘ * ’可以匹配零个或多个前缀元素。
函数isMatch(s, p)判断字符串s是否匹配正则表达式p。举些例子:
isMatch("aa","a") → falseisMatch("aa","aa") → trueisMatch("aaa","aa") → falseisMatch("aa", "a*") → trueisMatch("aa", ".*") → trueisMatch("ab", ".*") → trueisMatch("aab", "c*a*b") → true
与上一题相比,‘.’相当于上一题的‘?’,不同的是‘*’的表示,这一题表示的是任意个前缀元素。所以例子中的最后一个,“c*”可以表示有0个c或者多个c,所以这里的匹配时true,放在上一题那就是false。
思路
使用动态规划的方法来实现,类似大多数字符串比较问题的DP解法,该子问题dp[i][j]定义为字符串s的前i个字符和字符串p的前j个字符的匹配情况。数组dp[][]是一个bool数组,且大小是(slen + 1) x (plen + 1)
我们先假设字符串s和p都是从1到起始的。
初始情况:
- 首先将整个数组初始化为false,接下来考虑什么情况下能初始化为true;
- dp[0][0]是true,都是0个字符,当然为true;
- 对于dp[0][j],也就是s取0个字符,只有p的长度在增长。因为p的第一个字符不可能是‘*’(没有意义),所以只有当j大于1时且p的第j个数,即p[j]是‘*’时,dp[0][j]取决于dp[0][j-2];dp[0][j-2]表示不取‘*’的前缀元素时,能否匹配。
例子:p = a*b,当s取空,p在‘*’可以取0个a,此时dp[0][2]是true的。
状态转移:
1. 若p[j]是‘*’:
- 要么不取p[j]这个字符,则dp[i][j] = dp[i][j-2];
- 要么取p[j]这个字符,那么dp[i][j]取决于s的前i-1个字符与p的前j个字符匹配以及第i个字符和第j-1个字符的匹配情况,即dp[i][j] = dp[i-1][j] && (s[i] == p[j-1] || p[j-1] = ‘.’)
2. 若p[j]不是‘*’:
- dp[i][j]取决于s的前i-1个字符和p的前j-1个字符的匹配情况,以及s的第i个字符和p的第j个字符的匹配情况,即dp[i ][j] = dp[i - 1][j - 1] && (s[i] == p[j] || '.' == p[j]);
在代码实现时,要注意字符串s和p实际上是从0开始的,所以处理时要注意s和p取字符时的下标要减1.
代码:
if (s.length() == 0 && p.length() == 0)return true;if (p.empty())return false;//首先,如果p中没有任何通配符,那直接比较两个字符串即可if (p.find('.') == p.npos && p.find('*') == p.npos) {if (p == s)return true;elsereturn false;}int slen = s.length();int plen = p.length();bool dp[slen+1][plen+1]; //表示s的前i个字符和p的前j个字符是否匹配memset(dp, false, sizeof(dp));dp[0][0] = true;int i = 0, j = 0;for (int j = 2; j <= plen; j++)if (p[j - 1] == '*')dp[0][j] = dp[0][j - 2];for (i = 1; i <= slen; i++) {for (j = 1; j <= plen; j++) {// dp[i][j - 2]表示‘*’前的字符取0个;//或者 if (p[j - 1] == '*')dp[i][j] = dp[i][j - 2] || (s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j];elsedp[i ][j] = dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);}}return dp[slen][plen];
总结:
第一道题本来想用DP的,结果发现好像写起来比较麻烦= =结果就偷懒写成了回溯的形式。
第二道题,在进行数组dp计算前,加上没有通配符时,直接比较字符串的情况,使得时间一下子少了10ms,当然这跟测试用例也有关。但是我觉得这个判断也是有必要的,毕竟DP的过程耗费时间。
总的来说,虽然做了好几周DP的题目,比以前刚接触时好很多,但有时思路还是难以打开....还需努力啊
- Leetcode——44Wildcard Matching && 10 Regular Expression Matchi
- leetcode 10 Regular Expression Matching & 44 Wildcard Matching
- Leetcode 10, 44 Regular Expression Marching, Wildcard Matching
- leetcode 10|44. Regular Expression Matching 44. Wildcard Matching
- [LeetCode]Regular Expression Matching、Wildcard Matching
- 【LeetCode】Wildcard Matching && Regular Expression Matching
- 【LeetCode笔记】Wildcard Matching 和 Regular Expression Matching
- LeetCode —— Regular Expression Matching
- LeetCode——Regular Expression Matching
- LeetCode——Regular Expression Matching
- [LeetCode]—Regular Expression Matching 正则匹配
- LeetCode—10.Regular Expression Matching
- LeetCode题解——10Regular Expression Matching
- leetcode第10题——***Regular Expression Matching
- 算法w2——Regular Expression Matching(leetcode 10)
- 刷LeetCode(10)——Regular Expression Matching
- Leetcode【10】:Regular Expression Matching
- [leetcode 10] Regular Expression Matching
- iOS常用的加密方式
- Python 创建二维列表+追加元素+items()
- hdfs分布式搭建
- Git和Github的简单使用
- 关于Windows update 和停机码0x0000006B
- Leetcode——44Wildcard Matching && 10 Regular Expression Matchi
- Java源码集合类TreeMap学习1——数据结构4平衡二叉树创建代码
- [设计模式]享元模式(Flyweight) 共享池 连接池
- 成员变量、类变量、局部变量的区别
- Caused by: java.sql.SQLException: Column count doesn't match value count at row 1
- 中断线程的方法
- 摄像机标定学习笔记(4)
- Caused by: org.apache.ibatis.binding.BindingException: Parameter '__frch_item_0' not found. Availabl
- Eclipse导jar包方法