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。

这个状态转义方程要怎么推算出来呢?

  1. 如果p.charAt(i)=='*','*'可以选择匹配0个字符,此时flag[i][j]=flag[i-1][j];可以选择匹配1个字符,此时flag[i][j]=flag[i-1][j-1];……所以可以得到下面的公式:

  2. 因为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 上一次匹配的模式串的下标
  1. 如果当前相等或者模式串中字符为?,则移动相互的下标即可;
  2. 如果当前模式串字符为*,分别纪录lastmatch、laststar,并且移动模式串下标,但是不移动待匹配字符串下标,因为可能存在匹配0个字符串的情况;
  3. 如果当前相互对应的字符不再相等且不为*,如果前面有*号,说明之前的匹配失败了,模式字符串下标回到之前纪录laststar的后一位,不再移动,专门用来给待匹配字符串字符来匹配,这段时间内,si会不断的向前移动,直到匹配到相互的值相等才移动模式字符串的下标;
  4. 如果前面的情况都不符合,则肯定为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 题解的更多相关文章

  1. LeetCode: Wildcard Matching 解题报告

    Wildcard MatchingImplement wildcard pattern matching with support for '?' and '*'. '?' Matches any s ...

  2. [LeetCode] Wildcard Matching 外卡匹配

    Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...

  3. [Leetcode] Wildcard Matching

    Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...

  4. [leetcode]Wildcard Matching @ Python

    原题地址:https://oj.leetcode.com/problems/wildcard-matching/ 题意: Implement wildcard pattern matching wit ...

  5. [LeetCode] Wildcard Matching 字符串匹配,kmp,回溯,dp

    Implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. ...

  6. [Leetcode] Wildcard matching 通配符匹配

    Implement wildcard pattern matching with support for'?'and'*'. '?' Matches any single character. '*' ...

  7. leetcode Wildcard Matching greedy algrithm

    The recursive program will result in TLE like this: class Solution { public: bool isMatch(const char ...

  8. [LeetCode]Wildcard Matching 通配符匹配(贪心)

    一開始採用递归写.TLE. class Solution { public: bool flag; int n,m; void dfs(int id0,const char *s,int id1,co ...

  9. [Leetcode][Python]44:Wildcard Matching

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 44:Wildcard Matchinghttps://oj.leetcode ...

随机推荐

  1. 由Find All References引发的思考。,

    今天在研究C#代码问题的时候遇到了一个Visual Studio的小问题.在Visual Studio 2013中,使用Find All References功能不能找到同一类型不同版本的所有引用,具 ...

  2. Webdriver+Java实现使用cookie跳过登录

    Webdriver+Java实现使用cookie跳过登录   Webdriver模拟登录过程中很有可能遇到验证码,最近认真学习了下如何使用cookie直接跳过登录过程. 一.cookie的定义 来源百 ...

  3. ThinkPHP5.0更改框架的验证方法对象->validate(true)->save();

    我们更希望看到: // 新增对象至数据表 $result = $Teacher->validate(true)->save(); 而不是: // 新增对象至数据表 $result = $T ...

  4. 手把手教你用Eclipse+TestNG搭建接口自动化测试框架

    转载于:http://qa.blog.163.com/blog/static/190147002201510275306185/ 把群博里关于接口自动化的文章都看了一遍,都是关于测试过程中遇到的问题及 ...

  5. 使用windows 命令行执行Git clone时出现Host key error

    由于是在java中执行cmd命令调用git clone,导致git读取不到用户的ssh key,需要设置环境变量Home为正确的用户路径: cmd /c set HOME=C:/Users/你的用户名 ...

  6. SSO框架介绍前篇

    SSO全程是single sign on,即单点登录,是一种统一认证和授权机制.   单点登录,指访问同一服务器不同应用中的受保护资源的同一用户,只需要登录一次,即通过一个应用中的安全验证后,再访问其 ...

  7. GPUImage的filter 响应处理链 的理解笔记

    GPUImage的filter的textures处理链式结构 两个最重要的的地方: 最重要的一个类GPUImageOutput(所有的filter的父类,其他也有继承它的,如GPUImageUIEle ...

  8. python爬虫番外篇(一)进程,线程的初步了解

    一.进程 程序并不能单独和运行只有将程序装载到内存中,系统为他分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别在于:程序是指令的集合,它是进程的静态描述文本:进程是程序的一次执行活动, ...

  9. Java中的框架基础面试知识

    spring mvc 工作机制(原理): DispatcherServlet主要用作职责调度工作,本身主要用于控制流程 Spring mvc运行原理 1.springmvc将所有的请求都提交给Disp ...

  10. CSS3学习系列之选择器(三)

    E:enabled伪类选择器和E:disabled伪类选择器 E:enabled伪类选择器用来指定元素处于可用状态的样式. E:disabled伪类选择器用来指定当元素处于不可用状态时的样式. 当一个 ...