[LeetCode] Wildcard Matching 题解
6. 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") ? falseisMatch("aa","aa") ? trueisMatch("aaa","aa") ? falseisMatch("aa", "*") ? trueisMatch("aa", "a*") ? trueisMatch("ab", "?*") ? trueisMatch("aab", "c*a*b") ? false
解答
DFS
这里的难点在于如何处理*
,因为这个星号可以代表0到多个字符,而且有可能会遇到递归一开始匹配正确后面不正确,但实际上应该从后面开始匹配。
- class Solution(object):
- # p为匹配模式,s为字符串
- def recursive(self, s, p, si, pi, cur):
- first = True
- n_cur = cur
- while si < len(s) and pi < len(p) and (s[si] == p[pi] or p[pi] == '?'):
- si += 1
- pi += 1
- if pi == len(p):
- return si == len(s)
- if p[pi] == '*':
- while pi < len(p) and p[pi] == '*':
- pi += 1
- if pi >= len(p):
- return True
- for i in range(si, len(s)):
- # 表明开始重合,从这里再度开始递归
- if p[pi] != s[i] and p[pi] != '?':
- continue
- if first:
- cur += 1
- first = False
- # 可能存在多次重合但是还不算真正匹配的情况
- if self.recursive(s, p, i, pi, cur + 1):
- return True
- if cur > n_cur + 1: # 正常来说n_cur = cur + 1
- return False
- return False
- def isMatch(self, s, p):
- """
- :type s: str
- :type p: str
- :rtype: bool
- """
- return self.recursive(s, p, 0, 0, 0)
这种做法超时。
DP
我们定义一个二维数组dp,横坐标为待匹配字符串,纵坐标为模式字符串,dp[i][j]
则代表到模式字符串从0到 i 对应待匹配字符串的的0到 j 是否是匹配的。举个例子:
pattern = "a*bc"
str = "abbc"
我们可以根据前面提到的画出整个二维数组
\ | \ | a | b | b | c |
---|---|---|---|---|---|
\ | T | F | F | F | F |
a | F | T | F | F | F |
* | F | T | T | T | T |
b | F | F | T | T | F |
c | F | F | F | F | T |
我们可以发现一个规律,每当遇到两个字符不相等的时候,那么数组的值则肯定是False,相反相等的时候肯定是True,这里需要注意的是*
,这里则需要考虑到它当前可能匹配0个字符串或者匹配多个字符,比如上面中的a*
和ab
的情况,此时我们需要发现a*及a
或者a和ab
其中有任何一个成功匹配的,它的结果也肯定为T。
这个状态转义方程要怎么推算出来呢?
如果
p.charAt(i)=='*'
,'*'可以选择匹配0个字符,此时flag[i][j]=flag[i-1][j];可以选择匹配1个字符,此时flag[i][j]=flag[i-1][j-1];……所以可以得到下面的公式:因为
flag[i][j]=flag[i-1][j]||flag[i-1][j-1]||……||flag[i-1][0]
,我们可以代入上面的公式得到:
于是我们可以很简单的写出程序了(下面的程序的i,j和状态转义方程是相反的,但是原理是相同的)
- class Solution(object):
- # p为匹配模式,s为字符串
- def isMatch(self, s, p):
- """
- :type s: str
- :type p: str
- :rtype: bool
- """
- if len(s) != len(p) - p.count('*'):
- return False
- newp = ""
- i = 0
- while i < len(p):
- newp += p[i]
- if p[i] == '*':
- while i + 1 < len(p) and p[i + 1] == '*':
- i += 1
- i += 1
- sl, pl = len(s), len(newp)
- dp = [[False for x in range(pl + 1)] for y in range(sl + 1)]
- dp[0][0] = True
- if pl > 0 and p[0] == '*':
- dp[0][1] = True
- for x in range(1, sl + 1):
- for y in range(1, pl + 1):
- if newp[y - 1] != '*':
- dp[x][y] = dp[x - 1][y - 1] and (s[x - 1] == newp[y - 1] or newp[y - 1] == '?')
- else:
- dp[x][y] = dp[x - 1][y] or dp[x][y - 1]
- return dp[sl][pl]
同样的原理,我们还可以把它缩减成一维数组,你可以把它想象成在二维数组中计算每一行的数据,如果遇到*
则更新当前行的数据;为什么可以这么做呢?我们可以根据前面提到的公式发现,其中当前的数据依赖于j
的变化,也就是待匹配字符串的值,我们还需要在外面写个模式串的循环,其实和二维数组的做法的时间复杂度是一样的,但是缩减了空间,但是并不是所有的都可以这么做,这个取决于你的依赖项是什么。总而言之,其原理还是一样的,只是想办法让它们的数据能够共存到一维数组中。
- class Solution:
- # @return a boolean
- def isMatch(self, s, p):
- length = len(s)
- if len(p) - p.count('*') > length:
- return False
- dp = [True] + [False]*length
- for i in p:
- if i != '*':
- # 因为依赖项是前面的值,所以不能从前面往后面扫,得从后往前计算
- for n in reversed(range(length)):
- dp[n+1] = dp[n] and (i == s[n] or i == '?')
- else:
- # 更新当前行的数据
- for n in range(1, length+1):
- dp[n] = dp[n-1] or dp[n]
- dp[0] = dp[0] and i == '*'
- return dp[-1]
贪心算法
下标 | 描述 |
---|---|
si | 待匹配字符串的移动下标 |
pi | 模式串的移动下标 |
lastmatch | 上一次匹配的待匹配字符串的下标 |
laststar | 上一次匹配的模式串的下标 |
- 如果当前相等或者模式串中字符为
?
,则移动相互的下标即可; - 如果当前模式串字符为
*
,分别纪录lastmatch、laststar,并且移动模式串下标,但是不移动待匹配字符串下标,因为可能存在匹配0个字符串的情况; - 如果当前相互对应的字符不再相等且不为
*
,如果前面有*
号,说明之前的匹配失败了,模式字符串下标回到之前纪录laststar的后一位,不再移动,专门用来给待匹配字符串字符来匹配,这段时间内,si会不断的向前移动,直到匹配到相互的值相等才移动模式字符串的下标; - 如果前面的情况都不符合,则肯定为False;
看看我的抽象派画风。
- class Solution(object):
- # p为匹配模式,s为字符串
- def isMatch(self, s, p):
- si, pi = 0, 0
- lastmatch, laststar = -1, -1
- sl, pl = len(s), len(p)
- if pl - p.count('*') > sl:
- return False
- # 注意条件顺序
- while si < sl:
- if pi < pl and (s[si] == p[pi] or p[pi] == '?'):
- pi += 1
- si += 1
- elif pi < pl and p[pi] == '*':
- lastmatch, laststar = si, pi # 之所以不更新lastmatch是因为考虑到*只匹配0个字符串
- pi += 1
- # 再次进到这个判断,说明当前下标对应的值不相等
- elif laststar != -1:
- pi = laststar + 1 # pi当前不是*,并且回到上一次星的后面,专门用来给si匹配
- lastmatch += 1 # 必须更新lastmatch,因为之前已经不想等,如果在回到开始的状态就会陷入死循环
- si = lastmatch
- else:
- return False
- # 可能存在p的末尾都是*的情况
- while pi < len(p) and p[pi] == '*':
- pi += 1
- # 最后匹配成功模式字符串的下标必然为其长度,表示已经匹配完成
- return pi == pl
tips:不要小看保存你的长度值,如果你频繁的用到的话,最好保存下来,比如在这里,我保存下来以后可以让我提升%10的beat submissions!
一样的原理,但是使用了递归的方式来做
- class Solution(object):
- def isMatch(self, s, p):
- """
- :type s: str
- :type p: str
- :rtype: bool
- """
- seen = {}
- wild_single, wild_multi = "?", "*"
- # seen has the pattern - source tuple as key, and bool result as success
- source, pattern = s, p
- def is_match(sindex, pindex):
- key = (sindex, pindex)
- if key in seen:
- return seen[key]
- result = True
- # if there's no string, and pattern is not only * then fail
- if sindex >= len(source):
- for wildindex in xrange(pindex, len(pattern)):
- if pattern[wildindex] != wild_multi:
- result = False
- break
- # there's a string, but no pattern
- elif pindex >= len(pattern):
- result = False
- # if next pattern is multi though, that's something
- elif pattern[pindex] == wild_multi:
- # for zero, simply check sindex, pindex + 1
- result = is_match(sindex, pindex + 1) # just for easier debug
- # if zero, than it's a match
- # otherwise we need to check multi
- # for that, if char is not a wild, then it has to match the source,
- result = result or is_match(sindex + 1, pindex)
- else:
- # either a regular char, or wild_single
- result = (( pattern[pindex] == wild_single or pattern[pindex] == source[sindex]) and
- is_match(sindex + 1, pindex + 1))
- seen[key] = result
- return result
- if (len(p) - p.count(wild_multi) > len(s)):
- return False
- return is_match(0, 0)
[LeetCode] Wildcard Matching 题解的更多相关文章
- LeetCode: Wildcard Matching 解题报告
Wildcard MatchingImplement wildcard pattern matching with support for '?' and '*'. '?' Matches any s ...
- [LeetCode] Wildcard Matching 外卡匹配
Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...
- [Leetcode] Wildcard Matching
Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...
- [leetcode]Wildcard Matching @ Python
原题地址:https://oj.leetcode.com/problems/wildcard-matching/ 题意: Implement wildcard pattern matching wit ...
- [LeetCode] Wildcard Matching 字符串匹配,kmp,回溯,dp
Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...
- [Leetcode] Wildcard matching 通配符匹配
Implement wildcard pattern matching with support for'?'and'*'. '?' Matches any single character. '*' ...
- leetcode Wildcard Matching greedy algrithm
The recursive program will result in TLE like this: class Solution { public: bool isMatch(const char ...
- [LeetCode]Wildcard Matching 通配符匹配(贪心)
一開始採用递归写.TLE. class Solution { public: bool flag; int n,m; void dfs(int id0,const char *s,int id1,co ...
- [Leetcode][Python]44:Wildcard Matching
# -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 44:Wildcard Matchinghttps://oj.leetcode ...
随机推荐
- html或者php中 input框限制只能输入正整数,逻辑与和或运算
有时需要限制文本框输入内容的类型,本节分享下正则表达式限制文本框只能输入数字.小数点.英文字母.汉字等代码. 例如,输入大于0的正整数 代码如下: <input onkeyup="if ...
- cpp命名空间
1.namespace:c++里面的所有标识符都被定义到名为std的namespace中,命名空间就是标识符的各种可见范围,控制其作用域. 2.std为c++标准命名空间,c++标准库里的标识符都定义 ...
- Vue2.0 全家桶开发的网页应用(参照吾记APP)
github链接 借鉴吾记APP,使用 vue2.0+vue-router+vuex 为主要技术栈,elementui做为ui框架,多模块 spa 模式,webpack2.0 负责模块打包,gulp ...
- 点评阿里JAVA手册之编程规约(OOP 规约 、集合处理 、并发处理 、其他)
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文难度系数为三星(★★★) 本文为第二篇 第一篇 点评阿里JAVA手 ...
- HTML5笔记3——Web Storage和本地数据库
上一篇:HTML5笔记2——HTML5音/视频标签详解 Web Storage概述 在HTML5中,除了Canvas元素之外,另一个新增的非常重要的功能是可以再客户端本地保存数据的Web Storag ...
- Java线程间通信
1.由来 当需要实现有顺序的执行多个线程的时候,就需要进行线程通信来保证 2.实现线程通信的方法 wait()方法: wait()方法:挂起当前线程,并释放共享资源的锁 notify()方法: not ...
- git上传本地项目到github
git软件下载地址:https://git-scm.com/download/ 1. 在GitHub上建立项目登录GitHub后,你可以在右边靠中那里找到一个按钮“New Repository”,点击 ...
- NodeMCU入门(2):在线构建、刷入固件,上传代码
准备工作 1.NodeMCU模块 2.ESP8266Flasher.exe 3.ESPlorer v0.2.0-rc6 构建固件 Building the firmware提供了三种构建你自己固件的方 ...
- Unity3d—做一个年月日选择器(Scroll Rect拖动效果优化)— 无限滚动 + 锁定元素
最近..... 废话不多说上效果图 用的是UGUI 我先说思路 通过判断元素的位置信息来改变Hierarchy的顺序 实现无限滚动 改变位置的同时也要不断的调整Content的位置防止乱跳 元素锁定就 ...
- (转)CSS颜色及<a>标签超链接颜色改变
CSS颜色大全 <a>标签超链接颜色改变 A:hover {BACKGROUND-COLOR: #ffccff; COLOR: #0080ff} (hover表示鼠 ...