Leetcode算法系列| 10. 正则表达式匹配

目录

  • 1.题目
  • 2.题解
    • C# 解法一:分段匹配法
    • C# 解法二:回溯法
    • C# 解法三:动态规划

1.题目

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
1.‘.’ 匹配任意单个字符
2.‘.’ 匹配任意单个字符
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

  • 示例1:
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
  • 示例 2:
输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
  • 示例3:
输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
  • 提示:
    • 1 <= s.length <= 20
    • 1 <= p.length <= 20
    • s 只包含从 a-z 的小写字母。
    • p 只包含从 a-z 的小写字母,以及字符 . 和 *。
    • 保证每次出现字符 * 时,前面都匹配到有效的字符

2.题解

  • 映入脑海的第一个想法是将数字转换为字符串,并检查字符串是否为回文。但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。
  • 第二个想法是将数字本身反转,然后将反转后的数字与原始数字进行比较,如果它们是相同的,那么这个数字就是回文。
  • 但是,如果反转后的数字大于 int.MAX\text{int.MAX}int.MAX,我们将遇到整数溢出问题。
  • 按照第二个想法,为了避免数字反转可能导致的溢出问题,为什么不考虑只反转 int\text{int}int 数字的一半?毕竟,如果该数字是回文,其后半部分反转后应该与原始数字的前半部分相同。
    例如,输入 1221,我们可以将数字 “1221” 的后半部分从 “21” 反转为 “12”,并将其与前半部分 “12” 进行比较,因为二者相同,我们得知数字 1221 是回文。

C# 解法一:分段匹配法

  • 根据星号的位置将p切割为多个 尾星串 与 至多一个 无星串,然后从p头到p尾求M值。求解p的某段的M值时,需要根据上一段的M值来依次求解;若上一段M不包含任何值,则匹配失败。若p从头到尾走完了, 则判断最终的M中是否包含了 s.length-1 这个值, 若包含了,则s与p是匹配的。
public class Solution {public bool IsMatch(string s, string p){List<int> M1 = new List<int>() { -1 };while (p.Length > 0 && M1.Count > 0){List<int> M2 = new List<int>() { };int pStarIndex = p.IndexOf("*");foreach (int m1 in M1){string sRight = s.Substring(m1 + 1);if (pStarIndex == -1){var result = IsMatchNoStar(sRight, p);//无星说明是p的最后一组,若匹配成功则s与p匹配,直接返回true;否则直接continueif (result) return true;continue;}//有星,需要 找到 p[0,starIndex] 与 s 的匹配点//先判断p[0,starIndex-2]的字符与s的一一对应 int mNow = -1;bool isMatchBeforeStar = true;for (int i = 0; i < pStarIndex - 1; i++){if (i >= sRight.Length){isMatchBeforeStar = false; break;}mNow = i;if (!IsMatchChar(sRight[i], p[i])){isMatchBeforeStar = false; break;}}if (!isMatchBeforeStar) continue;//因为*可以表示0个,所以先将mNow添加到 匹配点集M2.Add(mNow + (m1 + 1));mNow++;//再看p[startIndex-1] 与 s的匹配点,找出所有的匹配点 while (mNow < sRight.Length && IsMatchChar(sRight[mNow], p[pStarIndex - 1])){M2.Add(mNow + (m1 + 1));mNow++;}}//将startIndex以及之前的串给舍弃,留下startIndex+1以及之后的串p = p.Substring(pStarIndex + 1);//更新M1 M1 = M2;}return M1.Contains(s.Length - 1);}public bool IsMatchNoStar(string s, string p){if (s.Length != p.Length){return false;}for (int i = 0; i < s.Length; i++){if (!IsMatchChar(s[i], p[i])){return false;}}return true;}public bool IsMatchChar(char sc, char pc){return ((pc == '.') || (sc == pc));}
}

可以牺牲部分可读性 来提高效率, 优化后的代码:

public class Solution {public bool IsMatch(string s, string p){List<int> M1 = new List<int>() { -1 };List<int> M2 = new List<int>() { };int pStart = 0;while (p.Length - pStart > 0 && M1.Count > 0){int pStarIndex = p.IndexOf("*", pStart);foreach (int m1 in M1){int sStart = m1 + 1;int sRightLen = s.Length - sStart;if (pStarIndex == -1){//IsMatchNoStarvar result = true;if (s.Length - sStart != p.Length - pStart) result = false;else{for (int i = 0; i < s.Length - sStart; i++){if (!((s[sStart + i] == p[pStart + i]) || (p[pStart + i] == '.'))){result = false;break;}}}if (result) return true;continue;}int mNow = -1;bool isMatchBeforeStar = true;int pLenBeforeStar = pStarIndex - pStart - 1;for (int i = 0; i < pLenBeforeStar; i++){if (i >= sRightLen){isMatchBeforeStar = false; break;}mNow = i;if (!((s[sStart + i] == p[pStart + i]) || (p[pStart + i] == '.'))){isMatchBeforeStar = false; break;}}if (!isMatchBeforeStar) continue;M2.Add(sStart + mNow++);int pCharIndexBeforeStar = pStarIndex - 1;while (mNow < sRightLen && ((s[sStart + mNow] == p[pCharIndexBeforeStar]) || (p[pCharIndexBeforeStar] == '.')))M2.Add(sStart + mNow++);}pStart = pStarIndex + 1;var tempMs = M1; M1 = M2; M2 = tempMs;M2.Clear();}return M1.Contains(s.Length - 1);}
}

1

  • 时间复杂度:O( pLen * sLen^2 )
    • 最坏的情况下,s全是同一个字母,p是同一个字母加星号,如 s=“aaaaaaa”,p= “aaa*” 。此时外层while以及找星的位置可以看作pLen,内层foreach每次都是对s的遍历,执行次数为sLen,再内层的 for与while加起来 又是对s的遍历,复杂度大概为 O( pLen * sLen^2 )
  • 空间复杂度:O( pLen * sLen )
    • 我第二层循环里面存在常数数量的变量定义,故为 O(pLen*sLen)

C# 解法二:回溯法

  • 回溯法解体的思路与分段匹配法类似,但使用递归后,只需要非常少的代码量。

  • 以p为主串,从左向右去匹配s,匹配成功的部分都去掉,也就是说 若最终s与p都变成了空串,则匹配成功。

  • 代码结构也很简单,首先是出口,然后是递归分支。

  • 出口EXIT:p变成空串时,若s也变成了空串,则匹配成功,否则匹配失败。

  • 分支A:p[1]为星号,直接去掉p的前两位,并递归。如 s=“b”,p=“a*b”.

  • 分支B:p[1]为星号时,若s第一位与p第一位匹配,去掉s第一位 , 并递归,如 “s=aab”,p=“ab"。否则匹配失败,如 s=“bba”,p="ab”.

  • 分支C:p[1]不为星号时,若s与p第一位匹配成功, 则都去掉第一位,并递归,如 s=“aab”,p=“aab*”. 否则匹配失败,如 s=“bab”, p=“aab*” .

  • 其中,当p[1]为星号时,分支A与分支B是【或】的关系,只要有一条成功,则匹配成功; 当p[1]不为星号时,就走C。

public class Solution {public bool IsMatch(string s, string p){//出口,EXITif (string.IsNullOrEmpty(p)) return string.IsNullOrEmpty(s); //Exitbool first_match = (   !string.IsNullOrEmpty(s) &&(p[0] == s[0] || p[0] == '.'));if (p.Length >= 2 && p[1] == '*')return (IsMatch(s, p.Substring(2)) ||       // A(first_match && IsMatch(s.Substring(1), p))); //Belse return first_match && IsMatch(s.Substring(1), p.Substring(1)); // C}
}

可以使用下标指针来代替SubString,从而明显的提高代码效率。 优化后的代码:

public class Solution {public bool IsMatch(string s, string p) { return IsMatch1(s, 0, p, 0); }public bool IsMatch1(string s, int sStart, string p, int pStart){if (pStart == p.Length) return sStart == s.Length; //Exitbool first_match = (sStart < s.Length &&(p[pStart] == s[sStart] || p[pStart] == '.'));if (p.Length - pStart >= 2 && p[pStart + 1] == '*')return (IsMatch1(s, sStart, p, pStart + 2) ||       // A(first_match && IsMatch1(s, sStart + 1, p, pStart))); //Belsereturn first_match && IsMatch1(s, sStart + 1, p, pStart + 1); // C}
}

2

  • 时间复杂度:O( (sLen+pLen) 2^(sLen+pLen/2) )*
  • 空间复杂度:O( (sLen+pLen) 2^(sLen+pLen/2) )*

C# 解法三:动态规划

public class Solution {public bool IsMatch(string s, string p){bool[,] dp = new bool[s.Length + 1, p.Length + 1];dp[s.Length, p.Length] = true;for (int i = s.Length; i >= 0; i--){for (int j = p.Length - 1; j >= 0; j--){bool first_match = (i < s.Length && (p[j] == s[i] || p[j] == '.'));if (j + 1 < p.Length && p[j + 1] == '*'){dp[i, j] = dp[i, j + 2]    //A|| first_match && dp[i + 1, j]; //B}else{dp[i, j] = first_match && dp[i + 1, j + 1]; // C}}}return dp[0, 0];}
}

3

  • 时间复杂度:O(sLen*pLen)
    • 最table中每个值会被计算一次,不会重复计算,而每个格子的计算时间可认为是O(1),所以总时间复杂度为O(sLen*pLen)
  • 空间复杂度:O(sLen*pLen)
    • able空间复杂度为 O(sLen*pLen).

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/823228.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【DevOps 工具链】日志管理工具 - 22种 选型(读这一篇就够了)

文章目录 1、简述2、内容分类3、归纳对比表&#xff08;排序不分先后&#xff09;4、日志管理主要目的5、日志管理工具 22种 详细&#xff08;排序不分先后&#xff09;5.1、ManageEngine EventLog Analyzer5.1.1、简介5.1.2、效果图5.1.3、日志管理架构5.1.4、EventLog Analyz…

HarmonyOS 路由传参

本文 我们来说两个page界面间的数据传递 路由跳转 router.pushUrl 之前我们用了不少了 但是我们只用了它的第一个参数 url 其实他还有个params参数 我们第一个组件可以编写代码如下 import router from ohos.router Entry Component struct Index {build() {Row() {Column() …

交互式笔记Jupyter Notebook本地部署并实现公网远程访问内网服务器

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下…

C编程指针篇----包括历年真题

一&#xff0c;&#xff08;20年&#xff09;用指针字符逆序 代码&#xff1a; int main() {char s[7] "monkey", * p1, * p2, c;p1 p2 s;while (*p2) p2;p2--;while (p2 > p1) {c *p1; *p1 *p2; *p2-- c; }printf("%s", s);return 0; } 运行结…

【华为机试】2023年真题B卷(python)-解密犯罪时间

一、题目 题目描述&#xff1a; 警察在侦破一个案件时&#xff0c;得到了线人给出的可能犯罪时间&#xff0c;形如 “HH:MM” 表示的时刻。 根据警察和线人的约定&#xff0c;为了隐蔽&#xff0c;该时间是修改过的&#xff0c;解密规则为&#xff1a; 利用当前出现过的数字&am…

jdk与cglib动态代理及原理

Spring的AOP在运行时多以jdk及cglib动态代理来实现。&#xff08;作者jdk是1.8版本&#xff09; 1 jdk 动态代理 Java中使用动态代理&#xff0c;只能对接口进行代理&#xff0c;不能对普通类进行代理。主要是由一个类及一个接口来实现&#xff1a; InvocationHandler&#…

【并发设计模式】聊聊等待唤醒机制的规范实现

在多线程编程中&#xff0c;其实就是分工、协作、互斥。在很多场景中&#xff0c;比如A执行的过程中需要同步等待另外一个线程处理的结果&#xff0c;这种方式下&#xff0c;就是一种等待唤醒的机制。本篇我们来讲述等待唤醒机制的三种实现&#xff0c;以及对应的应用场景。 G…

Python基础进阶3:函数和方法不是一回事

你好&#xff0c;我是kelly&#xff0c;今天分享的是Python的函数与方法的不同点。 对于Python的函数和方法是不一样的&#xff0c;这一点需要注意下。 一、结论 1、不存在隐式传参&#xff0c;所有参数都是显式传递的是函数。 2、存在隐式传参的是方法&#xff0c;一般指隐式…

神经元科技发布AI agent—“萨蔓莎”

今天神经元科技发布AI agent—“萨蔓莎“&#xff08;Samantha &#xff09;&#xff01; 取名“萨蔓莎”&#xff0c;是来自于一部讲述AI的电影《HER》。 电影讲述的是电影讲述男子西奥多汤布里&#xff08;Theodore Twombly&#xff0c;饰&#xff09;与拟人化萨曼莎&#…

日志记录、跟踪和指标

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 日志记录、跟踪和指标是系统可观察性的三大支柱。 下图显示了它们的定义和典型架构。 记录 日志记录系统中的离散事件。例如&#xff0c;我们可以将传入请求或对…

挑战Python100题(8)

100+ Python challenging programming exercises 8 Question 71 Please write a program which accepts basic mathematic expression from console and print the evaluation result. 请编写一个从控制台接受基本数学表达式的程序,并打印评估结果。 Example: If the follo…

蜘蛛目标检测数据集VOC格式3900张

蜘蛛是一类广泛分布于地球各地的节肢动物&#xff0c;它们属于蛛形纲动物&#xff0c;是无脊椎动物的一个大类。蜘蛛的身体通常分为两个部分&#xff0c;头胸部和腹部&#xff0c;与其他节肢动物相比&#xff0c;蜘蛛的身体相对较小。 蜘蛛具有典型的八只腿&#xff0c;它们的…

结构体:枚举

#include<iostream> using namespace std; int main() {enum weekday { mon, tus, wed, thu, fri, sat,sun }; //声明枚举类型 enum weekday day; //定义枚举变量 int a, b, c, d, e, f, g, loop; //定义整型变量 char ch A; //定义字符变量 f thu; //按照题意&a…

搜索关键字高亮

文章目录 思路分析具体实现源码 不知道大家平常有没有自己空闲的时候写一些小demo的习惯呢&#xff1f;我个人觉得&#xff0c;在空闲的时候时不时写一个小功能&#xff0c;日积月累&#xff0c;当你以后遇到需要使用的时候&#xff0c;就可以直接拿来使用&#xff0c;当然了。…

Java基础02-Java编程基础

文章目录 变量&#xff08;Variables&#xff09;局部变量和成员变量局部变量&#xff08;Local Variables&#xff09;成员变量&#xff08;Instance Variables&#xff09; 标识符&#xff08;Identifiers&#xff09;八种基本数据类型原始数据类型&#xff08;Primitive Dat…

ESP32:整合存储配网信息和MQTT笔记

文章目录 1.给LED和KEY的所用IO增加配置项1.1 增加配置文件1.2 修改相应的c源码 2. 把mqtt\tcp的工程整合到一起2.1 在何处调用 mqtt_app_start() 3. 测试MQTT4. 完整的工程源码 有一段时间没有玩ESP32&#xff0c;很多知识点都忘记了。今天测试一下MQTT&#xff0c;做个笔记。…

『番外篇六』SwiftUI 取得任意视图全局位置的三种方法

概览 在 SwiftUI 开发中,利用描述性代码我们可以很轻松的构建各种丰富多彩的视图。我们可以设置它们的大小、位置、颜色并应用不计其数的修改器。 但是,小伙伴们是否想过在 SwiftUI 中如何获取一个视图的全局位置坐标呢? 在本篇博文中,您将学到如下内容: 概览1. SwiftU…

C语言中灵活多变的动态内存管理,malloc函数 free函数 calloc函数 realloc函数

文章目录 &#x1f680;前言&#x1f680;管理动态内存的函数✈️malloc函数✈️free函数✈️calloc函数✈️realloc函数 &#x1f680;在使用动态内存函数时的常见错误✈️对NULL指针的解引用✈️ 对动态开辟空间的越界访问✈️对非动态开辟内存使用free释放✈️使用free释放一…

数据统计的一些专业术语学习

数据统计的一些专业术语学习 1. 极差2. 方差3. 标准差4. 均值绝对差 1. 极差 数据统计的极差&#xff0c;又称全距&#xff0c;是指一组数据中最大值和最小值之差。 举个例子&#xff0c;如果我们有一组数据&#xff1a;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c…

main参数传递、反汇编、汇编混合编程

week03 一、main参数传递二、反汇编三、汇编混合编程 一、main参数传递 参考 http://www.cnblogs.com/rocedu/p/6766748.html#SECCLA 在Linux下完成“求命令行传入整数参数的和” 注意C中main: int main(int argc, char *argv[]), 字符串“12” 转为12&#xff0c;可以调用atoi…