刻意练习:LeetCode实战 -- Task18. 正则表达式匹配

背景

本篇图文是LSGO软件技术团队组织的 第二期基础算法(Leetcode)刻意练习训练营 的打卡任务。本期训练营采用分类别练习的模式,即选择了五个知识点(数组、链表、字符串、树、贪心算法),每个知识点选择了 三个简单、两个中等、一个困难 等级的题目,共计三十道题,利用三十天的时间完成这组刻意练习。

本次任务的知识点:字符串

字符串或串(string) 是由数字、字母、下划线组成的一串字符。一般记为 s = “a1a2...an”(n >= 0)。它是编程语言中表示文本的数据类型。

通常以串的整体作为操作对象,如:在串中查找某个子串在该串中首次出现的位置、在串的某个位置上插入一个子串以及删除一个子串等。两个字符串相等的充要条件是:长度相等,并且各个对应位置上的字符都相等。串通常以顺序的方式进行存储与实现。


题目

  • 题号:10
  • 难度:困难
  • https://leetcode-cn.com/problems/regular-expression-matching/

给你一个字符串s和一个字符规律p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串s的,而不是部分字符串。

说明:

  • s可能为空,且只包含从 a-z 的小写字母。
  • p可能为空,且只包含从 a-z 的小写字母,以及字符 ‘.’ 和 ‘*’。

示例 1:

输入: 
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c'0, 'a' 被重复一次。因此可以匹配字符串 "aab"

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

实现

第一种:回溯法

思路:这种匹配思路其实就是不断地减掉sp的可以匹配首部,直至一个或两个字符串被减为空的时候,根据最终情况来得出结论。

如果只是两个普通字符串进行匹配,按序遍历比较即可:

if (s[i] == p[i])

如果正则表达式字符串p只有一种"."一种特殊标记,依然是按序遍历比较即可 :

if (s[i] == p[i] || p[i] == '.')

上述两种情况实现时还需要判断字符串长度和字符串判空的操作。

但是,"*"这个特殊字符需要特殊处理,当p的第i个元素的下一个元素是星号时会有两种情况:

  • i元素需要出现0次,我们就保持s不变,将p的减掉两个元素,调用IsMatch。例如s:bc、p:a*bc,我们就保持s不变,减掉p的"a*",调用IsMatch(s:bc,p:bc)
  • i元素需要出现一次或更多次,先比较i元素和s首元素,相等则保持p不变,s减掉首元素,调用IsMatch。例如s:aabb、p:a*bb,就保持p不变,减掉s的首元素,调用IsMatch(s:abb,p:a*bb)

此时存在一些需要思考的情况,例如s:abb、p:a*abb,会用两种方式处理:

  • 按照上述第二种情况比较i元素和s首元素,发现相等就会减掉s的首字符,调用IsMatch(s:bb,p:a*abb)。在按照上述第一种情况减去p的两个元素,调用IsMatch(s:bb,p:abb),最终导致false
  • 直接按照上述第一种情况减去p的两个元素,调用IsMatch(s:abb,p:abb),最终导致true

所以说这算是一种暴力方法,会将所有的情况走一边,看看是否存在可以匹配的情况。

  • 执行结果:通过
  • 执行用时:768 ms, 在所有 C# 提交中击败了 10.69% 的用户
  • 内存消耗:25.6 MB, 在所有 C# 提交中击败了 5.00% 的用户
public class Solution
{
    public bool IsMatch(string s, string p)
    {
        //若正则串p为空串,则s为空串匹配成功,s不为空串匹配失败。
        if (string.IsNullOrEmpty(p))
            return string.IsNullOrEmpty(s) ? true : false;

        //判断s和p的首字符是否匹配,注意要先判断s不为空
        bool headMatched = string.IsNullOrEmpty(s) == false
            && (s[0] == p[0] || p[0] == '.');

        if (p.Length >= 2 && p[1] == '*')
        {
            //如果p的第一个元素的下一个元素是*,则分别对两种情况进行判断
            return IsMatch(s, p.Substring(2)) ||
                (headMatched && IsMatch(s.Substring(1), p));
        }
        else if (headMatched)
        {
            //否则,如果s和p的首字符相等
            return IsMatch(s.Substring(1), p.Substring(1));
        }
        else
        {
            return false;
        }
    }
}

Python 语言

  • 执行结果:通过
  • 执行用时:1940 ms, 在所有 Python3 提交中击败了 6.96% 的用户
  • 内存消耗:13.6 MB, 在所有 Python3 提交中击败了 5.06% 的用户
class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        if len(p) == 0:
            return True if len(s) == 0 else False
        headMatched = len(s) != 0 and (s[0] == p[0] or p[0] == '.')

        if len(p) >= 2 and p[1] == '*':
            return self.isMatch(s, p[2:]) or (headMatched and self.isMatch(s[1:], p))      
        elif headMatched == True:
            return self.isMatch(s[1:], p[1:])
        else:
            return False

往期活动

LSGO软件技术团队会定期开展提升编程技能的刻意练习活动,希望大家能够参与进来一起刻意练习,一起学习进步!

  • Python基础刻意练习活动即将开启,你参加吗?
  • Task01:变量、运算符与数据类型
  • Task02:条件与循环
  • Task03:列表与元组
  • Task04:字符串与序列
  • Task05:函数与Lambda表达式
  • Task06:字典与集合
  • Task07:文件与文件系统
  • Task08:异常处理
  • Task09:else 与 with 语句
  • Task10:类与对象
  • Task11:魔法方法
  • Task12:模块

我是 终身学习者“老马”,一个长期践行“结伴式学习”理念的 中年大叔

我崇尚分享,渴望成长,于2010年创立了“LSGO软件技术团队”,并加入了国内著名的开源组织“Datawhale”,也是“Dre@mtech”、“智能机器人研究中心”和“大数据与哲学社会科学实验室”的一员。

愿我们一起学习,一起进步,相互陪伴,共同成长。

后台回复「搜搜搜」,随机获取电子资源!
欢迎关注,请扫描二维码:

你可能感兴趣的:(C#学习,数据结构与算法,Python学习)