刻意练习:LeetCode实战 -- Task30.通配符匹配
背景
本篇图文是LSGO软件技术团队组织的 第二期基础算法(Leetcode)刻意练习训练营 的打卡任务。本期训练营采用分类别练习的模式,即选择了五个知识点(数组、链表、字符串、树、贪心算法),每个知识点选择了 三个简单、两个中等、一个困难 等级的题目,共计三十道题,利用三十天的时间完成这组刻意练习。
本次任务的知识点:贪心算法
贪心算法(greedy algorithm),又称贪婪算法,是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。
贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是局部最优解能决定全局最优解。简单地说,问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。
贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。
题目
- 题号:44
- 难度:困难
- https://leetcode-cn.com/problems/wildcard-matching/
给定一个字符串(s
) 和一个字符模式(p
) ,实现一个支持'?'
和'*'
的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母,以及字符?
和*
。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
示例 3:
输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
示例 4:
输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
示例 5:
输入:
s = "acdcb"
p = "a*c?b"
输出: false
示例 6:
输入:
"abefcdgiescdfimde"
"ab*cd?i*de"
输出:true
示例 7:
输入:
"aaaa"
"***a"
输出:true
实现
第一种:双索引法
我们用i
和j
分别标记s
和p
的第一个字符下标,即都初始化为0。用istart
和jstart
分别标记s
和p
中'*'
匹配过的位置,即初始化为-1
。
和普通字符串匹配的思路差不多,已经匹配成功的部分就不再考虑了,所以要用i
和j
标记当前正在比较的字符;但是最近匹配过的'*'
可能会被重复使用去匹配更多的字符,所以我们要用istart
和jstart
分别标记s
和p
中最近匹配过'*'
的位置。
- 如果
i
和j
标记的字符正好相等或者j
字符是'?'
匹配成功,则"移除"i
和j
元素,即自增i
、j
。 - 否则如果
j
字符是'*'
依然可以匹配成功,则用istart
和jstart
分别标记i
元素和j
元素,自增j
。 - 再否则如果
istart>-1
说明之前匹配过'*'
,因为'*'
可以匹配多个字符,所以这里要再次利用这个最近匹配过的'*'
匹配更多的字符,移动i
标记istart
的下一个字符,再让istart
重新标记i
元素,同时移动j
标记jstart
的下一个字符。 - 上述三种情况都不满足,则匹配失败,返回
false
。
最后当s
中的字符都判断完毕,则认为s
为空,此时需要p
为空或者p
中只剩下星号的时候,才能成功匹配。
- 执行结果:通过
- 执行用时:92 ms, 在所有 C# 提交中击败了 95.00% 的用户
- 内存消耗:25.7 MB, 在所有 C# 提交中击败了 66.67% 的用户
public class Solution
{public bool IsMatch(string s, string p){//若正则串p为空串,则s为空串匹配成功,s不为空串匹配失败。if (string.IsNullOrEmpty(p))return string.IsNullOrEmpty(s) ? true : false;int i = 0, j = 0, istart = -1, jstart = -1, plen = p.Length;//判断s的所有字符是否匹配while (i < s.Length){//三种匹配成功情况以及匹配失败返回falseif (j < plen && (s[i] == p[j] || p[j] == '?')){i++;j++;}else if (j < plen && p[j] == '*'){istart = i;jstart = j;j++;}else if (istart > -1){i = istart + 1;istart = i;j = jstart + 1;}else{return false;}}//s中的字符都判断完毕,则认为s为空//此时需要p为空或者p中只剩下星号的时候,才能成功匹配。//如果p中剩余的都是*,则可以移除剩余的*while (j < plen && p[j] == '*'){j++;}return j == plen;}
}
第二种:动态规划
dp
数组的含义:dp[i,j]
意思是s
的前i
个元素能否被p
的前j
个元素成功匹配。
知道了dp
数组的含义之后,我们就知道了初始化细节:
bool
类型的dp
数组,大小是[s.length+1,p.length+1]
,因为存在s
前0个字符和p
前0个字符的情况,即s
为空串或p
为空串。dp[0,0]
一定是true
,因为s
空串和p
空串是可以匹配成功的;dp[1,0] ~ dp[s.length,0]
一定都是false
,因为s
不为空串而p
为空串是不能匹配成功的。dp[0,1] ~ dp[0,p.length]
当s为空串的时候,而p
不是空串的时候,当且仅当p
的j
字符以及前面都为'*'
才为true
。dp[s.length,p.length]
就得到了s
和p
最终的匹配情况。
有了上述理解之后,就可以初始化dp
数组了。
然后填写dp
数组剩余部分即可,状态转移方程:
- 当
s[i] == p[j]
或者p[j] == '?'
,则dp[i,j] = dp[i-1,j-1]
。可以理解为当前字符成功匹配后,只需要考虑之前的字符串是否匹配即可。 - 当
p[j] == '*'
,则dp[i,j] = dp[i-1,j] || dp[i,j-1]
。可以理解为当字符为'*'
的时候会出现两种情况,第一种是'*'
需要作为一个字母与s[i]
进行匹配;第二种是'*'
需要作为空字符(即不需要'*'
可以直接"移除"),所以dp[i,j-1]
;用逻辑或将两种情况连接,是因为只要有一种情况可以匹配成功则当前匹配成功,有点暴力算法的感觉。 - 最后当
s[i] !=p [j] && p[j] != '*'
,dp[i,j] = false
。这步可以省略,因为dp
数组元素的默认值就是false
,所以不必要进行显式的赋值为false
。
有了上面的理解,我们就可以写代码了。
- 执行结果:通过
- 执行用时:112 ms, 在所有 C# 提交中击败了 62.50% 的用户
- 内存消耗:28.6 MB, 在所有 C# 提交中击败了 22.22% 的用户
class Solution
{public bool IsMatch(string s, string p){if (string.IsNullOrEmpty(p))return string.IsNullOrEmpty(s) ? true : false;int slen = s.Length, plen = p.Length;bool[,] dp = new bool[slen + 1, plen + 1];//初始化dp数组//dp[1][0]~dp[s.length][0]默认值flase不需要显式初始化为falsedp[0, 0] = true;//dp[0][1]~dp[0][p.length]只有p的j字符以及前面所有字符都为'*'才为truefor (int j = 1; j <= plen; j++){dp[0, j] = p[j - 1] == '*' && dp[0, j - 1];}//填写dp数组剩余部分for (int i = 1; i <= slen; i++){for (int j = 1; j <= plen; j++){char si = s[i - 1], pj = p[j - 1];if (si == pj || pj == '?'){dp[i, j] = dp[i - 1, j - 1];}else if (pj == '*'){dp[i, j] = dp[i - 1, j] || dp[i, j - 1];}}}return dp[slen, plen];}
}
往期活动
LSGO软件技术团队会定期开展提升编程技能的刻意练习活动,希望大家能够参与进来一起刻意练习,一起学习进步!
- Python基础刻意练习活动即将开启,你参加吗?
- Task01:变量、运算符与数据类型
- Task02:条件与循环
- Task03:列表与元组
- Task04:字符串与序列
- Task05:函数与Lambda表达式
- Task06:字典与集合
- Task07:文件与文件系统
- Task08:异常处理
- Task09:else 与 with 语句
- Task10:类与对象
- Task11:魔法方法
- Task12:模块
我是 终身学习者“老马”,一个长期践行“结伴式学习”理念的 中年大叔。
我崇尚分享,渴望成长,于2010年创立了“LSGO软件技术团队”,并加入了国内著名的开源组织“Datawhale”,也是“Dre@mtech”、“智能机器人研究中心”和“大数据与哲学社会科学实验室”的一员。
愿我们一起学习,一起进步,相互陪伴,共同成长。
后台回复「搜搜搜」,随机获取电子资源!
欢迎关注,请扫描二维码:
相关文章:

iOS 11 安全区域适配总结
2019独角兽企业重金招聘Python工程师标准>>> 导语:本文主要是对iOS 11下APP中tableView内容下移20pt或下移64pt的问题适配的一个总结。内容包括五个部分:问题的原因分析、adjustContentInset属性的计算方式、什么情况下的tableView会发生内容…

(广州)软件开发定制服务,工作流引擎 OA 库存管理系统
本人专注于工作流的研究设计同时提供软件开发定制服务,工作流引擎 OA系统 库存管理系统 如果有机会合作共事请联系:15817167503(本人在广州) QQ:1311663711 加时请注明软件定制 广州软件定制开发 转载于:https://www.cnblogs.com/…

Java类加载机制详解【java面试题】
Java类加载机制详解【java面试题】 (1)问题分析: Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户…

C#获取文件的当前路径
1. System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName -获取模块的完整路径。 2.System.Environment.CurrentDirectory -获取和设置当前目录(该进程从中启动的目录)的完全限定目录。 3.System.IO.Directory.GetCurrentDirectory() &a…

c# ThreadPool 判断子线程全部执行完毕的四种方法
1、先来看看这个多线程编程多线程用于数据采集时,速度明显很快,下面是基本方法,把那个auto写成采集数据方法即可。using System;using System.Collections.Generic;using System.Text;using System.Threading;namespace ConsoleApplication1{…

腾讯精选练习 50 题(Leetcode)笔记 PDF下载!
昨天在知识星球中立了一个Flag,第一步采取的行动就是把以前刷的“腾讯精选练习 50 题”重新梳理一下,就有了今天这本170多页的小册子。 这本小册子即可以作为学习数据结构与算法课程的参考资料,也可以作为备考计算机类研究生的备考资料。希望…

Python培训:try-except语句与else子句联合使用处理可能出现的程序异常
异常处理的主要目的是防止因外部环境的变化导致程序产生无法控制的错误,而不是处理程序的设计错误。因此,将所有的代码都用try语句包含起来的做法是不推荐的,try语句应尽量只包含可能产生异常的代码。Python中try-except语句还可以与else子句…

Backup Exec 2012 备份和还原活动目录(非授权还原)
延续以上两篇,安装配置完毕后,开始进行备份操作。 环境一如上篇: DC: pdc1.fengdian.info BE2012 Svr: backup.fengdian.info 本例使用BE2012对活动目录进行备份和后续的还原操作,通过模拟误删除DC中的两个OU及其用户账号,使用先…

hdu3368 Reversi
题意:一种翻转棋游戏,对当前的棋局,问黑子下一步最多能将几个白子翻为黑子,(当前黑子与原先棋盘中的黑子的连线中间的白子会翻成黑子) 分析:很简单的搜索题,不过一开始一直WA&#x…

有符号整型的数据范围为什么负数比正数多一个?
背景 我们先看Leetcode的这道题目: 标题:50. Pow(x, n)难度:中等https://leetcode-cn.com/problems/powx-n/ 实现 pow(x, n) ,即计算 x 的 n 次幂函数。 示例 1: 输入: 2.00000, 10 输出: 1024.00000示例 2: 输入: 2.10000, 3…

UI设计培训:UI构思创意技巧和方法
想要作为一名合格的UI设计师,那么创意技巧和方法是非常重要的,很多刚入职场的新人或者是工作多年的设计师都会在创意技巧和方法上遇到瓶颈,下面小编为大家整理一些UI构思创意技巧和方法,希望能够帮助到大家。 UI设计培训ÿ…

博客园 cnblogs博客添加Google Analytics统计
在cnblogs的文章列表中只可以看到自己的每篇文章的页面浏览量,没有详细的统计信息。Google Analytics作为强大的统计工具,能得到几乎所有想要的统计信息,是博客不可多得的好工具,本文介绍如何在cnblogs博客中使用Google Analytics…

技术图文:Python 位运算防坑指南
背景 我们先看这个题目: 标题:137. 只出现一次的数字 II难度:中等https://leetcode-cn.com/problems/single-number-ii/ 给定一个 非空 整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出…

聊聊nginx报错499问题
序本文主要来聊一下nginx的access log当中出现的499问题。 问题描述499 CLIENT CLOSED REQUESTA non-standard status code introduced by nginx for the case when a client closes the connection while nginx is processing the request. 原因服务器返回http头之前ÿ…

UI设计需要报培训班学习吗
UI设计在很多企业已经是不可或缺的一个岗位了,所以UI设计的发展空间是非常大的,想要做UI设计师,光靠自学是不行的,那么UI设计需要报培训班学习吗?来看看下面小编的详细介绍就知道了。 UI设计需要报培训班学习吗?目前学习UI设计主…

技术图文:位运算技术在求解算法题中的应用
背景 前段时间,在知识星球立了一个Flag,这是总结Leetcode刷题的第一篇图文。 在总结这篇图文的时候,顺便把遇到的坑写了两篇辅助的图文,大家可以参考一下: 有符号整型的数据范围为什么负数比正数多一个?P…

JavaScript学习系列6 充实文档的内容
JavaScript 两项基本原则渐进增强:你应该总是从最核心的部分,也就是从内容开始。应该根据内容使用标记实现良好的结构;然后再逐步加强这些内容。平稳退化:不支持JavaScript也能访问基本内容。内容:我们在Html文件中编辑…

mapreduce中设置自定义的输入类,进行文本解析(默认以tab键为分隔符)
job.setInputFormatClass(KeyValueTextInputFormat.class);//此时map端输入的键的内容为第一个tab键以左的内容,值得内容为第一个tab键以右的内容转载于:https://www.cnblogs.com/le-ping/p/7788973.html

如何分辨Web前端培训机构的好坏
web前端在互联网行业有着非常高的薪水和很好的前景,想要学习web前端的人越来越多,对于web前端培训机构的选择让很多人犯了难,那么如何分辨Web前端培训机构的好坏呢?该如何选择呢?来看看下面的详细介绍。 如何分辨Web前端培训机构的好坏? 1…

mysql dba系统学习(19)配置mysql+lvs+keeplived实现Mysql读操作的负载均衡
配置mysqllvskeeplived实现Mysql读操作的负载均衡 环境: test1192.168.46.131master test2192.168.46.130slave备份test库 test3调度器 1、安装与配置Keepalived 首先在节点test1、test2上安装Keepalived软件,软件安装非常简单。 12345678910111213[root…
技术图文:浅析 C# Dictionary实现原理
背景 对于 C# 中的 Dictionary类 相信大家都不陌生,这是一个 Collection(集合) 类型,可以通过 Key/Value (键值对) 的形式来存放数据;该类最大的优点就是它查找元素的时间复杂度接近 O(1),实际项目中常被用来做一些数据的本地缓存…

思念水饺吃成泡沫水饺(图)思念质量门
思念再曝水饺吃出泡沫 !思念带着“创可贴汤圆”和“泡沫水饺”“拜晚年”了,而失去新国标的“护身符”,思念这次还要找出什么样的借口为汤圆里的创可贴和水饺里的泡沫找“台阶”下呢?思念汤圆刚被爆吃出创可贴,思念水饺…

jQuery动画的显示与隐藏效果
jQuery中用于控制元素显示和隐藏效果的方法如表1所示。 表1 控制元素的显示和隐藏 在表1中,参数speed表示动画的速度,可设置为动画时长的毫秒值(如1000),或预定的3种速度(slow、fast和normal);参数easing表示切换效果,默认效果为s…

技术图文:字典技术在求解算法题中的应用
背景 前段时间,在知识星球立了一个Flag,这是总结Leetcode刷题的第二篇图文。 在总结这篇图文的时候,顺便总结了 C# 中Dictionary类的实现,大家可以参考一下: 浅析 C# Dictionary实现原理 理论部分 C# 中字典的常…

[WCF REST] 解决资源并发修改的一个有效的手段:条件更新(Conditional Update)
条件获取(Conditional Update)可以避免相同数据的重复传输,进而提高性能。条件更新(Conditional Update)用于解决资源并发操作问题。如果我们预先获取一个资源进行修改或者删除,条件更新检验帮助我们确认资…
Netty 之 Zero-copy 的实现(下)
上一篇说到了 CompositeByteBuf ,这一篇接着上篇的讲下去。 FileRegion 让我们先看一个Netty官方的example // netty-netty-4.1.16.Final\example\src\main\java\io\netty\example\file\FileServerHandler.java public void channelRead0(ChannelHandlerContext ctx…

Java中final关键字如何使用?
final变量只能赋值一次,赋值的方式有三种: 1)声明变量时直接赋值; 2)非静态成员变量在{}块中赋值,静态成员变量在static{}块中赋值; 3)非静态成员变量在构造方法中赋值。 final修饰类 final类不能被继承,因此不会有子类。final类中…

技术图文:双指针在求解算法题中的应用
背景 前段时间,在知识星球立了一个Flag,这是总结Leetcode刷题的第三篇图文。 理论部分 Python list 的源码地址: https://github.com/python/cpython/blob/master/Include/listobject.h https://github.com/python/cpython/blob/master/O…

【CSON原创】HTML5游戏框架cnGameJS开发实录(外部输入模块篇)
返回目录 1.为什么我们需要外部输入模块? 在游戏中我们常常用到类似这样的操作:鼠标点击某位置,玩家对象移动到该位置,或者按鼠标方向键,玩家向不同方向移动,等等。这些操作无一不用与外部输入设备打交道。…

中国科协(深圳)海外人才离岸创新创业基地源创力中心开业,主打国际创业服务...
2017年9月28日,由深圳市科学技术协会主办、深圳市罗湖区人民政府支持,深圳市源创力离岸创新中心承办的“梧桐山基地开园仪式暨梧桐湾未来论坛”于深圳举办。 据介绍, “中国科协(深圳)海外人才离岸创新创业基地”是在深…