LeetCode44,Hard,从搜索到动态规划的详细推导

GLM-5.1开源模型:面向工程落地的长周期自主编码代理 Coding Agent(编码智能体)是当前AI编程范式的核心演进方向,其本质是将大语言模型与工具调用、状态管理、目标规划深度耦合,实现从‘问答式辅助’到‘无人值守工程交付’的跃迁。关键技术原理包括MoE稀疏激活架构以平衡算力与成本、交错思考机制解决长程任务中的状态漂移、保留思考能力克服传统RAG在工程上下文中的目标遗忘。这类模型的技术价值在于显著降低重复性开发负担、提升遗留系统重构效率、保障代码交付质量一致性。典型应用场景涵盖自动化Bug修复、跨框架项目迁移、CI/CD智能代码审查及文档-代码同步维护。G 阅读详情

本文始发于个人公众号:TechFlow,原创不易,求个关注


今天是LeetCode专题的第24篇文章,我们一起来看LeetCode的44题——Wildcard Matching,这是一道Hard难度的问题,会稍稍有点难,但是好消息是没有出现我们之前没见过的算法。

题意很简单,给定两个字符串s和p,其中s是母串,p是模式串。简单解释一下这两个概念,这两个概念一般出现在字符串匹配的问题当中。有些同学可能不太理解,我们打个不恰当的比方,我们可以把母串想象成锁,把模式串想象成钥匙。一些万能钥匙可以打开多把锁,也就是说钥匙是可以变化的,锁是固定的。我们要判断的就是模式串能不能匹配上母串,也就是钥匙能不能打开锁。

在模式串p当中可能出现两种特殊字符,一种是?,它表示匹配任何单一字符。还有一种是*,它表示匹配任何一串字符,包括空字符串。所以这个*看起来非常强大,一个可以任意匹配。但是这题当中有一个限制,s和p必须要是完全匹配,不能是部分匹配。也就是说p当中的所有字符都要用上,举个例子,s串是aab,p串是*c。虽然*就足够匹配s串的所有内容,但是由于之后跟了一个c没有用上,所以不能算是合法匹配。

为了简化说明,我们用si表示s串的第i个字符,pi表示p串的第i个字符。

搜索

我们还是先从简单的方法构思,最先思考暴力的解法,很容易发现在这道题当中没有办法简单粗暴地暴力计算。原因也很简单,因为当出现*这个符号的时候,我们不知道它究竟应该匹配多长的字符串。可以是0,也可以是长度任意一只匹配到结尾。

为了解决这个问题,最好的办法就是都试一试,枚举一下*这个符号应该匹配的长度。但是由于*的数量可能不止一个,如果出现多个的话,由于前后会互相影响,并且前后之间存在顺序,也就是说*这个符号如何匹配的决策不是一起做的,而是有先后的。就和八皇后当中的皇后不是一起摆放的,而是有先后顺序的,如果我们把*看成是皇后,其实这已经是比较明显的搜索问题了。

如果想通了,把这题当成是搜索问题来解决的话,其实并不算困难,其实就是简单的枚举。

我们搜索s和p串匹配的位置,我们用si表示s串当前需要匹配的字符,pi表示p串当前需要匹配的字符。如果pi位置的字符不是*,那么情况比较有限,我们只需要判断上下界以及是否匹配即可。如果pi的位置是*,那么情况会复杂一些,因为*的出现意味着三种决策。

第一种决策是不使用*来匹配当前位置的si,在这种情况下意味着我们抛弃这个*,但是si还是没有找到匹配的对象,所以si不能移动,从这点出发,我们可以得到下一个转移到的位置是si,pi+1。即p当中的指针移动了一位,但是s中的指针保持不动,等待继续匹配。

第二种决策是只匹配当前的si,不再匹配si之后的内容。在这种情况下,我们只需要把*当成是一个正常的字符处理就好了,那么我们应该转移到的位置是si+1,pi+1。也就是说s和p当中的指针各自移动了一位。

第三种决策是*不止匹配当前的si,并且还会继续往下匹配,在这种情况下我们需要保持pi的位置不动,只移动si,也就是说我们会转移到si+1和pi。

但是我们仔细分析一下,会发现其实这三种情况是可以合并的。在第二种情况中,我们只匹配当前位置,其实这等价于我们在当前位置执行第三种策略,在转移之后的位置执行策略1。

举个例子:比如s串是abc,p串是a*c。我们在匹配第二个字符的时候,执行策略3,于是跳转到2, 1。也就是si是c,pi仍然是*,这个时候我们再执行策略1,则会跳转到2,2,和我们在一开始的时候执行策略2是一致的。这样在*出现很多的时候可以减少决策过程,起到加速的作用。

最后,由于这是一道搜索答案存不存在的问题,并不需要我们找到所有的解。在这种情况下,使用bfs会比dfs效率更高,但遗憾的是这两种方法我都试过了,都无法通过,因为会超时。可能这是Python的原因(解释型语言执行效率低),因为我用C++是可以过的。

虽然过不了,但是思路是对的,不介意的话可以看下代码:

class Solution:
    
    def isMatch(self, s: str, p: str) -> bool:
        n, m = len(s), len(p)
        if m == 1 and p[0] == '*':
            return True
        import queue
        que = queue.Queue()
        que.put((0, 0))
        while not que.empty():
            si, pi = que.get()
            if si == n and pi == m:
                return True
            # 如果s串已经匹配完了,p串还没结束
            # 需要特判一下p串当中是否是*
            # 如果p串的末尾都是*,那么也是可以匹配的
            if si == n:
                if p[pi] == '*':
                    que.put((si, pi + 1))
                    continue
                continue
            elif pi >= m:
                continue
            # 如果没有出现*,那么我们判断能否匹配
            elif p[pi] == '?' or p[pi] == s[si]:
                que.put((si + 1, pi + 1))
                continue
            # 否则的话分两种情况往下
            elif p[pi] == '*':
                que.put((si, pi + 1))
                que.put((si+1, pi))
        return False

动态规划

虽然上面的方法无法通过,但其实已经给了我们很多启发了,如果我们把上面提到的si和pi看成是状态的话,虽然我们是用bfs实现的,但本质上已经是动态规划的思想了。

在一些动态规划的问题当中,我们也是通过队列来维护状态的。我们会将合法的状态全部存储在队列当中,当我们通过状态和决策转移到新的合法状态的时候,我们继续插入队列。所以从这个角度来说动态规划和搜索问题本质是类似的,或者更准确一点说动态规划只是一种思想,它实现的载体可以多种多样,既可以是使用数组递推,也可以是使用数据结构维护状态。这一点非常重要,也是我们动态规划进阶的基础。

我们为什么会超时?

如果我们把刚才搜索的思路看成是动态规划的话,会发现这是一个顺推的动态规划。简单介绍一下,动态规划的状态和转移虽然每题当中都不尽相同,但是整体的思路只有两种。一种叫做顺推,一种是逆推。

在顺推思路当中,我们记录所有合法的状态,然后从合法的状态出发,通过决策进行转移,将转移得到的状态记录下来留待后续继续转移。如果我们把这些状态串联起来的话,会发现我们是顺着一个逻辑上的顺序执行的,所以称为顺推。逆推则恰恰相反,我们遍历未知的状态,通过状态之间的转移关系,探索它的源头,从而确定当前未知状态的结果。也就是说一个是从已知到未知,另一个是先获得未知再探索它已知的来源。

为什么在这题当中顺推不行呢?因为当*出现的时候,我们继续往下推进的状态当中仍然有*。比如当我们决定使用*来匹配si的时候,我们会转移到si+1,pi状态。在后序的状态当中同样包含了*,又会面临更多的状态。这些状态中间很多会出现重叠,并且这些重叠的状态当中有很多是剪枝也无法解决的,比如p串当中一连串的*。这个时候我们就可以换换角度思考一下逆推了。

常规套路,我们用dp[i][j]记录s[:i+1]和p[:j+1]匹配的状态,那么这个dp[i][j]可能是哪些状态转移过来的呢?其实非常有限,如果si和pi能够匹配,那么它可以从dp[i-1][j-1]转移过来。如果不匹配,我们需要判断一下pi是否是*。如果是的话,则有两种情况,一种是*匹配空,把si交给pi-1,所以可以从dp[i][j-1]转移得到。另一种是匹配si,由于*可以匹配的数量不止一个,所以这时候可以从dp[i-1][j]转移得到。由于每个i,j的状态来源非常有限,只有有限的几种,所以我们完全可以枚举所有的i,j进行倒推,这样的复杂度就是O(nm),是完全可控的。

把上面这段话梳理清楚的话,这就是一个非常明显的动态规划问题了。

我们来看代码,从代码当中获得更多细节吧。

class Solution:
    
    def isMatch(self, s: str, p: str) -> bool:
        n, m = len(s), len(p)
        if m == 1 and p[0] == '*':
            return True
        s = ' ' + s
        p = ' ' + p
        # 注意下标,第一个range里的是列数,第二个range里的是行数
        # 申请一个dp[n][m]的数组
        dp = [[False for _ in range(m+2)] for _ in range(n+2)]
        dp[0][0] = True
        # i从0开始,因为p的开头出现*可以匹配空串
        for i in range(0, n+1):
            for j in range(1, m+1):
                # si和pj匹配
                if s[i] == p[j] or p[j] == '?':
                    dp[i][j] = dp[i-1][j-1] if i > 0 else False
                    continue
                # pj出现*,这时候判断上游能否有合法的转移
                if p[j] == '*':
                    dp[i][j] = (dp[i-1][j] if i > 0 else False) | dp[i][j-1]
        return dp[n][m]

这段代码虽然短,但是其中的细节不少,包括下标以及边界等条件。所以建议大家理解思路之后上手写过再来对照代码进行思考,相信会有更多收获。因为很多细节可能是需要看到了错误的case进行思考之后才会明白,这也是LeetCode当中一个比较好的点,可以看到测试数据,可以知道自己错在哪里。

今天的文章就是这些,另外在次条当中分享了vscode当中LeetCode的插件使用方法,感兴趣的同学不要错过,最后,祝大家刷题愉快。

如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。
在这里插入图片描述

【leetcode热题100】通配符匹配 给你一个输入字符串 (s) 和一个字符模式 (p) ,请你实现一个支持和'*''*'判定匹配成功的充要条件是:字符模式必须能够输入字符串(而不是部分匹配)。false"a" 无法匹配 "aa" 整个字符串。true'*' 可以匹配任意字符串。a"false' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。 阅读详情

相关推荐

【信息科学与工程学】【数据科学】数据科学领域 第六篇 算法设计 07 堆、栈、表、矩阵、集合、队列、数组、字符串、树、图、动态规划、结构体、元组的算法01

当前段必须包含所有字符的最后出现位置。1. 字符串分割成最多片段2. 基因序列功能域划分3. 文本段落划分4. 音频声道分离5. 图像色彩分区6. 社交网络社群划分7. 蛋白质结构域8. 音乐乐章分段9. 代码模块划分10. 章节划分特征:一次扫描,贪心确定边界。

weixin_49199313的博客 1266

44. Wildcard Matching

Problem Description: Given an input string (s) and a pattern (p), implement wildcard pattern matching with support for'?'and'*'. '?' Matches any single character. '*' Matches any sequence of characters (including the empty sequence). The matching ...

zzyy17的专栏 239

leetcodehard是什么意思-ARTS:ARTS:(也就是Algorithm、Review、Tip、Share简称ARTS)

leetcode hard是什么意思 (1)每周至少做一个 leetcode 的算法题 (2)阅读并点评至少一篇英文技术文章 (3)学习至少一个技术技巧 (4)分享一篇有观点和思考的技术文章 ARTS的初衷 Algorithm。主要是为了编程训练和学习。每周至少做一个 leetcode 的算法题(先从Easy开始,然后再Medium,最后才Hard)。进行编程训练,如果不训练你看再多的算法书,你依然不会做算法题,看完书后,你需要训练。关于做Leetcode的的优势,你可以看一下我在coolshell上的文章 Leetcode 编程训练 - 酷 壳 - CoolShell。 Review:主要是为了学习英文,如果你的英文不行,你基本上无缘技术高手。所以,需要你阅读并点评至少一篇英文技术文章,我个人最喜欢去的地方是 Tip:主要是为了总结和归纳你在日常工作中所遇到的知识点。学习至少一个技术技巧。你在工作中遇到的问题,踩过的坑,学习的点滴知识。 Share:主要是为了建立你的影响力,能够输出价值观。分享一篇有观点和思考的技术文章。这就是ARTS的全部动机。 提示一下,我们不要以交作业的心态来

每日一题:Leetcode-44 通配符匹配

力扣题目解题思路java代码。

LIUCHANGSHUO的博客 565

对冲策略与马丁结合ea的编程实践(五)核心是把两个品种合成一个新品种

在外汇、数字货币、期货交易甚至赌博中,马丁策略、网格策略是最常用的策略,也是最容易被理解的策略,但是如何克服单边行情、极端行情,是使用马丁策略最为头疼的事情,如加大间距、加大资金量,只能缓解,并不能根本解决这样的情况,所以使用时大部分时候都心里没底。 有没有一种正确的思路,让行情控制在震荡范围以内呢,这是今天我要向大家分享的一个思路,那就是对冲。大家看下图: 大家可以看到欧美(EURUSD)和镑美(GBPUSD),这两个品种,大部分时候走势是一致的,那么我们有没有可能把他们两个品种进行一下合成,比如买0.

omidsky的博客 3738

Selenium ChromeDriver反爬与性能优化实战指南

ChromeDriver作为Selenium的核心驱动,本质是浏览器自动化执行引擎,其默认行为会暴露大量JS运行时指纹(如navigator.webdriver、window.chrome),极易被前端反爬系统识别;同时因进程管理粗放、渲染等待机制僵化,导致内存泄漏、僵尸进程和高并发下稳定性崩塌。本文围绕真实生产场景,系统解析浏览器指纹伪造原理、进程级资源管控策略、基于页面状态的智能等待范式,并提供Docker容器化、K8s弹性扩缩与Prometheus可观测性落地路径,助力开发者将ChromeDriver

weixin_29762151的博客 1259

LeetCode第44题----通配符匹配

本题在LeetCode官方中的难度评级为Hard,归属类型为动态规划。 文章目录前言一、题目描述二、解题思想和解题步骤三、代码实现总结 前言 动态规划一直是算法中的难点,难在每道题虽然有大致相同的特点,但是又有独到之处,让人难以寻找其中的规律,也就是说想汇总成一套非常完整的模板基本是不可能的。本文将利用动态规划来解决LeetCode第44题,该题非常之典型,希望可以帮到大家,让我们能从题解中领悟到动态规划的中心思想,找到一套属于自己的解决动态规划的套路。为了方便大家更容易领悟动态规划,可以参考本人的另一

F & F的博客 1007

ADS入门学习笔记——低噪声放大器(2)

1、输入匹配网络; 2、输出匹配网络;

weixin_42126696的博客 4988

LeetCode算法 —— 通配符匹配

题目: 给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。 ‘?’ 可以匹配任何单个字符。 ‘*’ 可以匹配任意字符串(包括空字符串)。 两个字符串完全匹配才算匹配成功。 说明: s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。 示例 1: 输入: s = “aa” p = “a” 输出: false 解释: “a” 无法匹配 “aa” 整个字符串。 示例 2: 输入: s = “aa” p =

浪子花梦 1161

Feko仿真GPS微带天线学习笔记

为了优化方便,使参数变量化,所以定义几个变量在CADFEKO左侧浏览器中如图所示定义变量,如下表贴片的尺寸切角尺寸feed =-6.4馈电点距坐标原点的距离sub_w=45基板的宽度sub_d=45基板的长度sub_h=5基板的高度epsR=5.6介电常数损耗角正切。

qq_36521926的博客 3988

使用HART Server PC软件进行HART设备的组态、调试和分析的完整指南

HART协议是一种用于工业自动化中的双向通信协议,能够在4-20mA模拟信号的基础上叠加数字信号,从而实现设备的远程监控和管理。HART协议兼具模拟和数字通信的优点,被广泛应用于过程控制系统中。设备组态:通过软件可以对HART设备进行组态,配置设备参数。设备调试:支持设备的在线调试和故障诊断,方便快速解决问题。数据分析:提供设备运行数据的实时监控和历史数据分析功能。报表生成:支持生成各类报表,便于数据记录和管理。

qq_38334677的博客 2468

LeetCode044——通配符匹配

我的LeetCode代码仓:https://github.com/617076674/LeetCode 原题链接:https://leetcode-cn.com/problems/wildcard-matching/description/ 题目描述: 知识点:递归、动态规划 思路一:递归实现(在LeetCode中提交会超时) 本题和LeetCode010——正则表达式匹配很像,但又...

清风阁 4532

SAR图像去斑(Despeckling)研究的主流开源数据集

这些数据集专门用于训练监督学习去斑模型,提供了配对的含斑SAR图像和对应的无噪声参考图像(Ground Truth)。这些数据集主要用于目标检测/识别,但包含原始SAR图像,可用于。由于缺乏真实无噪声SAR图像,许多研究采用。

VectorShift的博客 128

[leetcode] 44. 通配符匹配

[leetcode] 44. 通配符匹配

lvhao123456789的博客 1378

移植Google Gemini Nano到RK3588 NPU,实现高效边缘推理

在嵌入式 AI 开发中,大家常常会遇到模型在硬件平台上运行时出现性能瓶颈的问题,比如推理速度慢、功耗过高,这严重影响了应用的实时性和稳定性 ,导致性能瓶颈,无法满足如智能安防、工业检测等场景的实时性需求。为了解决这些问题,我们可以将轻量模型移植到具有强大算力的硬件平台上。本文将手把手教你将 Google Gemini Nano(或类似轻量模型)移植到 RK3588 NPU,充分发挥 RK3588 NPU 的 6Tops 算力,实现高效的模型推理,提升嵌入式系统的 AI 处理能力。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 293

【技能】伪真openclaw主动式智能体—告别‘巨婴’AI工具人:体验主动智能体的魅力

摘要: 传统AI常因被动执行、缺乏闭环而令人抓狂。Proactivity主动执行能力将AI从"指令跟随者"升级为"主动合伙人",具备预判需求、自动推进、断聊恢复、问题自解等能力。其核心原理在于建立AI专属工作体系(持久化内存、动态任务层、临时缓存层、归档复盘层),通过7步闭环(感知触发→状态恢复→边界校验→方案探索→动作决策→执行落地→状态留存)确保安全高效。

海兰 207

Leetcode之通配符匹配

问题描述 给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '' 的通配符匹配。 '?' 可以匹配任何单个字符。 '' 可以匹配任意字符串(包括空字符串)。 两个字符串完全匹配才算匹配成功。 说明: s 可能为空,且只包含从 a-z 的小写字母。 p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。 示例 1: 输入: s = "aa" p =...

qq_44711190的博客 651

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

LeetCode分类刷题----动态规划

将石头尽量分成大小相等的两堆,然后分配一个sum/2的容量的背包去装石头这样遍历完整个数组后,背包里装的是尽可能多的石头。装完了之后让剩下的一堆石头减去这堆石头就是剩余的最小的石头了。return 0;//dp数组代表当前容量所能装的最大值i++) {j--) {//将石头分成两堆,用大的一堆减去小的一堆 }

池鱼 1207

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

LeetCode题练习与总结:通配符匹配

在本文中,我们探讨了如何实现一个支持通配符 '?' 和 '*' 的字符串匹配算法。通过递归方法,我们详细分析了匹配过程,包括处理 '*' 字符的三种情况,以及字符比较的逻辑。最后,我们讨论了代码的时间复杂度和空间复杂度,以及可能的尾递归优化。

一直学习永不止步 1578

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【leetCode】128. 最长连续序列(hard)

一、题目描述 给定一个未排序的整数数组,找出最长连续序列的长度。 要求算法的时间复杂度为 O(n)。 示例: 输入: [100, 4, 200, 1, 3, 2] 输出: 4 解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。 二、解题思路 & 代码 2.1 哈希表 class Solution: def longestConsecutive(self, nums): longest_streak = 0 num_set = set(nums

weixin_41888257的博客 970

第一次在leetcode上做hard级别的题目

今天第一次在LeetCode做hard级别的题目:Median of Two Sorted Arrays. 题目大意是这样的:给两个排好序的数组,让找出它们合成一个数组的时候的中位数。数组的长度分别为m,n,要求时间复杂度为O(log(m+n))。本来拿到这个题目,一眼看过去感觉有点熟悉,有点归并排序的影子。如果真是归并排序的话,这已经是最后一步。但是题目要求的时间复杂度是O(log(m+n))

qiweiqian的博客 2902

老卫带你学---leetcode刷题(44. 通配符匹配)

给你一个输入字符串 (s) 和一个字符模式 § ,请你实现一个支持 ‘?’ 和 ‘’ 匹配规则的通配符匹配:’ 可以匹配任何单个字符。’ 可以匹配任意字符序列(包括空字符序列)。判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。示例 1:输入:s = “aa”, p = “a”输出:false解释:“a” 无法匹配 “aa” 整个字符串。示例 2:输入:s = “aa”, p = "输出:true解释:'’ 可以匹配任意字符串。

老卫带你学 474

leetcode hard

#TitleSolutionAcceptanceDifficultyFrequency 4 Median of Two Sorted Arrays 27.2% Hard 10 Regular Expression Matching 25.6% Hard ...

weixin_30853329的博客 342

LeetCode 44:通配符匹配

设dp[i][j]字符串s的前i个字符s[0..i-1]模式串p的前j个字符p[0..j-1]是否完全匹配。

m0_75126572的博客 1196

leetcode:44. 通配符匹配

题目来源 leetcode 题目描述 题目解析 双序列型动态规划 (1)确定状态 设A的长度为m,B的长度为n,现在我们考虑A和B如何匹配 关注最后的字符,主要取决于B[n-1]是什么 如果B[n-1]是一个正常字符,则如果A[m-1] = B[n-1],能否匹配取决A[0…m-2]和B[0…n-2]是否匹配;否则不能匹配 如果B[n-1]是?,则A[m-1]一定是和?匹配,之后能否匹配取决于A[0...m-2]和B[0...n-2]是否匹配 如果B[n-1]是*,则它可以匹配0个或者任意多个

OceanStar的博客 260

LeetCode题目44:通配符匹配【python】

本文介绍了解决LeetCode题目44 "通配符匹配"的三种方法:动态规划、双指针和贪心算法,详解实现步骤和代码,提供算法分析

10年数据\经营分析经验,现任大厂数据分析负责人 2486
上一篇: 在vscode中配置LeetCode插件,从此愉快地刷题
下一篇: LeetCode45——从搜索算法推导到贪心
TechFlow
博客等级 码龄7年 347粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值