eetode正则表达式匹配(动态规划)弗兰克的猫

给定一个字符串 (s) 和一个字符模式 (p)。实现支持 '.' 和 '*' 的正则表达式匹配。

'.' 匹配任意单个字符。

'*' 匹配零个或多个前面的元素。

匹配应该覆盖整个字符串 (s) ,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。示例 1:

示例 2:

示例 3:

示例 4:

示例 5:

题目难度:⭐⭐⭐

这是一道有点难度的题,如果你看了一遍题目之后,没有什么好的想法,不用心急,深呼吸,让我们一起来探索如何解决这道题。

其实题目的要求,就是实现一个最简单的正则表达式,即.与*的匹配,一提到正则表达式,你也许会想到形如 ^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$ 之类的一大串乱七八糟的代码,觉得看着都蛋疼,还要让我来实现???emmmm,不要方,问题不大,不要被正则表达式这个名号给吓到,要相信,问题总比方法多🤣。何况这里只需要解析两个特殊字符,岂不是小菜一碟。

明人不说骚话,撸起袖子就开干。

先重新阅读一遍题目,对题目要求的理解和把握很关键,这决定了之后的思考会不会跑偏,后面的几个示例可以用来验证自己理解是否正确。

从后面给的栗子里可以看出,题目的意思是要求字符串s与字符模式p能完全匹配才能算是通过,而不是在s中找到一个p能匹配的子字符串。

脑袋一拍,那一个字符一个字符来匹配不就完事了?嗯,先试试看。把题中的栗子拿出来画成图,然后进行观察。

在形成自己的思路后,一定要对这几个栗子进行验证,不然代码写完以后才发现理解错了题目的意思就很尴尬了。🌝

对于一个位于字符模式p中的字符c来说,只有三种情况:

我们先来看第一种情况,当c == '.'的时候,因为可以匹配任意字符,那么,直接跳过即可,对于第三种情况,那么只要s中对应的字符字符c相同即可,你看,很简单吧,我们已经完成三分之二了。接下来,再来看看最后一种情况。

如果c == *,那么代表可以匹配零个或者多个前面的字符,比如a*可以匹配a、aaaa、aaaaa也可以匹配空字符,所以它其实是个修饰符,用来修饰它前面的字符,必须要跟其他字符一起使用,所以在我们在一个个遍历模式串中的字符的时候,还需看看后面跟的字符是不是*,如果是的话,那么就要进行特殊处理了。

*代表匹配0个或多个它前面的字符,所以有两种情况,一种是0个,一种是多个。

梳理一下思路,每次从p中拿出一个字符来与s中的字符进行匹配,如果该字符后续的字符不是*,那么直接与s中对应字符进行匹配判断即可,如果匹配上了,那么就将两个游标都往后移动一位。如果匹配过程中遇到不相等的情况,则直接返回false。如果后续字符是*,那么就如上面所分析的,分成两种情况,一种是匹配0个,那么只需要跳过p中的这两个字符,继续与s中的字符进行比较即可,如果是匹配多个,那么将s中的游标往后移动一个,继续进行判断,这两个条件只要其中一个能满足即可。

对于上面分析*字符的说明也许还不够清晰,继续画图:

等等,你有没有闻到一丝递归的味道,既然对于每个在模式串中的字符都可以采用相同的策略进行处理,那不就是暗示这里可以使用递归吗。机智如我😝

先来写一下伪代码来继续理清思路,毕竟这可是一道复杂度为三星级别的题,万万不可轻敌。

emmm,这个伪代码好像不太合格,几乎把代码写完了,23333,接下来只需要考虑一下边界情况,把代码补全就行了,当然,还可以将代码美化一下:

大功告成,提交一下。

emmm,递归的效率一般都比较差,只击败了28%的用户。

当然,一般能用递归解决的地方,都可以使用非递归的方式解决,下面,我们来使用另一种解决方案。

动态规划???emmm,如果你不经常接触算法的话,也许对这个名词不太熟悉,所以我先简单的介绍一下。

动态规划,简单来说就是,动态的去进行,规划。😂言归正传,其实动态规划也是一种分治的思想,将问题分解成一个个子问题,通过解决所有子问题,来求得原问题的解,一般用于求解最优问题。但是跟分治法不同的地方在于,动态规划的子问题往往是相互关联的,拿最简单的斐波拉契数列来说,我们使用分治的思想,对于求fib(6),使用的公式是fib(6) = fib(5) + fib(4),于是将原来的问题便转化为求解fib(5)和fib(4),继续递归,fib(5) = fib(4) + fib(3),然后再继续递归fib(4) = fib(3) + fib(2) 、fib(3) = fib(2) + fib(1) 这里fib(1) = 1 和 fib(2) = 1为初始条件,于是就能求出fib(6),初看起来似乎没什么毛病,但是仔细想一想,由于每次递归都是无状态的,所以其实做了很多重复的计算,画个图来感受一下:

动态规划就可以很好的解决这个问题,动态规划的思想跟上面是一样的,但不同的是,动态规划会将每次计算的结果存起来,因此就解决了。简单一点理解,就是在分治的基础上加入了一个状态数组,来存储中间计算的结果,以减少重复计算的耗时。当然,动态规划又分为两种,一种是自顶向下,就是刚才所说的方法,另一个种是自底向上,还是拿上面的斐波拉契数列来说,要计算fib(6),因此我们先计算fib(3) = fib(2) + fib(1) ,再计算fib(4) = fib(3) + fib(2) 和fib(5) = fib(4) + fib(3),这样,就能算出fib(6) = fib(5) + fib(4)的结果了。

在动态规划中有几个比较关键的概念:子问题,状态,状态空间,初始状态,状态转移方程。

子问题:与原问题形式相同或者类似,只不过规模变小了,子问题都解决后,原问题即解决。

状态空间:由所有状态构成的集合,上面的栗子比较简单,状态空间是一维空间。

状态初始条件:即状态的初始状态,上面的栗子里fib(1) = 1和fib(2) = 1就是初始条件。

状态转移方程:用来表示状态之间是如何转换的方程,即如何从一个或者多个已知的状态求出另一个状态,可以使用递推公式表示。上面栗子的公式为fib(n) = f(n - 1) + f(n -2) (n > 2)

关于动态规划的介绍就结束了,接下来我们来看如何在这道题上面使用。

我们先来考虑自顶向下的算法。为方便起见,假定使用符号s[i:]表示字符串s中从第i个字符到最后一个字符组成的子串,p[j:]则表示模式串p中,从第j个字符到最后一个字符组成的子串,使用 match(i,j) 表示s[i:]与p[j:]的匹配情况,如果能匹配,则置为true,否则置为false。这就是各个子问题的状态。

那么对于match(i,j)的值,取决于p[j + 1]是否为'*'。

这样表述一下是不是就清晰了不少。

以s = "aab"; p = "c*a*b"为例,先构建一个二维状态空间来存储中间计算得出的状态值。横向的值代表i,纵向的值代表j,match(0,0)的值即问题的解,用f代表false,t代表true。

接下来描述一下后续的计算过程:

你看,其实很简单吧。😅

接下来转化成代码:

来跑一下结果:

击败了99.95%,不错不错。

已经很晚了,但我还是想把另一种方法也一起写完。🙄

还有一种方法,叫做自底向上方法,也是动态规划中的一种,这种方法的思路其实很简单粗暴,即从最后一个字符开始反向匹配,还是以刚才的栗子为例,从i = 3, j = 5 开始依次往左往上循环计算,match(3,5) == true,核心的逻辑并没有变。因为最边缘的值的匹配都是可以直接计算出来的,下面推算其中的一部分:

剩下的部分可以自行推导。代码如下:

提交一下:

效率也是相当高的,虽然比自顶向下方法多计算了不少值,但是减少了方法调用次数,省去了多次递归调用方法的开销,而且每次计算的过程相当简单,所以并不能说它的效率比自顶向下的方法低,要视具体情况而定。

写到这,今天的题总算是完成的差不多了,长呼一口,来回顾一下今天的收获吧:

首先我们用分治法,使用递归来解决,但是效率偏低。

于是我们用了动态规划的思想来解决这个问题,与分治法最大的不同便在于动态规划会存储中间的计算状态,以减少重复计算。

先是用了自顶向下的方法,跟分治法几乎没有差异,只是多使用了一个二维数组。

接着用自底向上的方法来解决,从最后的字符开始匹配,将多次递归调用转为在一个循环体中完成。

THE END
0.每日一难day3)以下哪些通配符只能匹配单个字符本文讲解了如何使用动态规划算法解决字符串s与字符模式p的通配符匹配问题,包括‘?’和‘*’的特殊含义及其匹配规则。通过实例和代码实现,深入理解匹配过程和边界情况。 44. 通配符匹配 给定一个字符串(s) 和一个字符模式 § ,实现一个支持 ‘?’和‘*’ 的通配符匹配。 ‘?’ 可以匹配任何单个字符。 ‘*’ jvzquC41dnuh0lxfp0tfv8|gkzooa=7273<:38ftvkimg8igvcomu86466965@=
1.给你一个输入字符串(s)和一个字符模式(p),请你实现一个支持给定一个字符串(s) 和一个字符模式 (p) ,实现一个支持'?'和'*'的通配符匹配。 '?' 可以匹配任何单个字符。 '*' 可以匹配任意字符串(包括空字符串)。 一键获取完整项目代码 1 2 两个字符串完全匹配才算匹配成功。 说明: s可能为空,且只包含从a-z的小写字母。 jvzquC41dnuh0lxfp0tfv8vsa3=67989;1gsvrhng1jfvjnnu1>53B65:4
2.给定一个字符串S,以及一个模式串P,所有字符串中只包含大小写为了解决这个问题,我们需要高效地查找模式串P在主串S中所有出现的位置。由于数据规模较大(M可达$10^6$),我们不能使用暴力匹配方法。推荐使用KMP 算法(Knuth-Morris-Pratt)来解决此类子串匹配问题。 ✅ KMP 算法简介 KMP 是一种高效的字符串匹配算法,其核心思想是: jvzquC41ygtlw7hufp4og}4cpu}ft88i:3wfv~h9
3.iOSLeetCode☞通配符匹配苹果匹配代码本文解析如何使用动态规划算法解决字符串s与包含'?'和'*'通配符的模式p匹配问题。通过状态转移方程,理解小写字母、问号和星号的匹配规则,实现高效匹配。 给定一个字符串(s) 和一个字符模式 (p) ,实现一个支持'?'和'*'的通配符匹配。 '?' 可以匹配任何单个字符。 '*' 可以匹配任意字符串(包括空字符串)。 jvzquC41dnuh0lxfp0tfv8qkswt{jjsi1cxuklqg1fkucrqu13774<:;84
4.字符串模式匹配:KMP算法讲解71字符串的模式匹配本文深入讲解KMP算法的原理及应用,重点介绍如何利用模式串的特性提高匹配效率,避免无效匹配,给出优化后的next数组求解方法及完整的Java代码实现。 一、模式匹配问题的定义 字符串的模式匹配问题指的是:给定一个字符串和一个模式串,搜索模式串在中第一次出现的位置。假设字符串 “” 为被搜索的字符串,字符串 “” jvzquC41dnuh0lxfp0tfv8OcemeDLc4ctvodnn4fgvgjn|4996793B=
5.经典测试工程师面试题(三)小丸子给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '' 的通配符匹配。'?' 可以匹配任何单个字符。'' 可以匹配任意字符串(包括空字符串)。两个字符串完全匹配才算匹配成功。 复制代码 说明: s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符jvzquC41yy}/ewgnqiy/exr1xkil{4r13993<6880nuou
6.LeetCode10.正则表达式匹配腾讯云开发者社区给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。 代码语言:javascript 代码运行次数:0 运行 AI代码解释 '.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖整个字符串s的,而不是部分字符串。 说明: s 可能为空,且只包含从 a-z jvzquC41enuvf7ygpekov7hqo1jfxnqqrgx0c{ykenk03?9;57<
7.字符串的基本操作和模式匹配实践串的基本操作以及模式匹配本文介绍了一个简单的字符串操作程序,包括字符串的创建、初始化、赋值、比较、连接、获取子串及模式匹配等功能,并提供了完整的C语言代码实现。 #include <stdio.h>#include <stdlib.h>#define MAX_SIZE 100#define OK 1#define ERROR -1typedefintStatus; jvzquC41dnuh0lxfp0tfv8jpigxmc8ftvkimg8igvcomu8<:48;589
8.C语言输入字符串的4种方法(附带示例)在C语言中,输入字符串是一个常见的操作,但对于初学者来说可能会遇到一些困惑。本文将详细介绍几种C语言输入字符串的方法,并解释每种方法的优缺点以及使用场景。 C语言中最常用的输入字符串的函数有 scanf()、gets()、fgets()、scanf_s() 和 gets_s()。我们将逐一介绍这些函数的使用方法,并探讨它们的特点。 jvzquC41e0hjcwhjgpm/pny1xkkx1myd2h{70qyon
9.和'*'的正则表达式匹配本文介绍了一种支持‘.’和‘*’的正则表达式匹配算法,详细解析了匹配过程中的各种情况及对应的处理策略,包括如何处理模式中的‘*’字符。 问题描述: 给定一个字符串 (s) 和一个字符模式 (p)。实现支持 ‘.’和‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符。 jvzquC41dnuh0lxfp0tfv8Icng[q~4ctvodnn4fgvgjn|4:35;44<;
10.算法:动态规划之字符串模式匹配字符串匹配动态规划一、问题描述 二、常规算法 三、动态规划算法 总结 提示:以下是本篇文章正文内容,下面案例可供参考 一、问题描述 给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配 '?' 可以匹配任何单个字符。 '*' 可以匹配任意字符串(包括空字符串)。 两个字符串完全匹配才算匹配成功jvzquC41dnuh0lxfp0tfv8vnaiung8ftvkimg8igvcomu86575>639;
11.LeetCode每日一题通配符匹配文章浏览阅读389次。题目给定一个字符串(s)和一个字符模式(p),实现一个支持jvzquC41dnuh0lxfp0tfv8knqth295291gsvrhng1jfvjnnu1732;:9;9;
12.831.KMP字符串文章浏览阅读137次。给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。求出模式串 P 在字符串 S 中所有出现的位置的起始下标。next数组:模式串[1,i]jvzquC41dnuh0lxfp0tfv8vsa6?94:=8;1gsvrhng1jfvjnnu173:?=9;6:
13.LeetCode44.通配符匹配(εNFA解法)leetcode44给定一个字符串(s ss) 和一个字符模式 (p pp) ,实现一个支持′ ? ′ '?'′?′和′ ∗ ′ '*'′∗′的通配符匹配。 '?'可以匹配任何单个字符。'*'可以匹配任意字符串(包括空字符串)。 AI写代码c 运行 1 2 如: " ∗ a ∗ b " "*a*b""∗a∗b"可以匹配" a d c e b " "jvzquC41dnuh0lxfp0tfv8|{p3;76=;678>0c{ykenk0fnyckny03;89;7;6:
14.模式字符串查找(支持通配符‘*’)leetcode44.通配符匹配给定一个字符串(s) 和一个字符模式 (p) ,实现一个支持'?'和'*'的通配符匹配。 '?'可以匹配任何单个字符。 '*'可以匹配任意字符串(包括空字符串)。 AI写代码 两个字符串完全匹配才算匹配成功。 说明: s可能为空,且只包含从a-z的小写字母。 jvzquC41dnuh0lxfp0tfv8|gkzooa<:8766:88ftvkimg8igvcomu86355<76@<