大厂笔试题——leetcode10正则表达式匹配(动态规划)
xsobi 2025-01-06 16:07 1 浏览
题目描述
先点赞再观看、帅哥靓女养成好习惯。
正则表达式匹配
给你一个字符串 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 = "cab"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "misisp*."
输出: false
递归(超时)
这题刚开始见到,还以为遇到原题了,因为跟剑指offer的其中一题非常像,剑指offer第52题正则表达式,只不过那题给的两个char类型的数组,当时弱弱的用递归暴力过了。
然后一顿操作把上次递归的方法重写过来,结果超时了……
但是还是把这种递归的思路讲一下,递归主要进行匹配所有情况,主要是看当前位置两个串串能不能匹配。
需要考虑*情况可以匹配,因为*可以出现0次,1次多次。那么在遇到使用*的如果匹配了,可以通过递归实现下面三者方式
- 它可以使用0次(相当于跟字符串下一部分匹配 a*aa和aa这个第一个a*可以看成0次)
- 也可以使用1次(在当前往后移例如a*aa和aaa转成aa和aa的匹配)
- 也可以使用多次(例如a*和aaa转成a*和aa的匹配)
同样,如果遇到*不可以匹配,那么就使用0次就行了(b*aa和aa匹配转换成aa和aa匹配)
如果下一个不是*,那就考虑是否相当或者模式字符是否为.进行继续匹配或者终止就可以,在考虑一些开始结束情况就可以了,一个大概的思维导图可以看一下。
这部分实现的代码如下:
public static boolean isMatch2(String s, String p) {
//System.out.println(s+" "+p);
if (p.length() == 0)// 模式串为false
{
if (s.length() == 0)
return true;
return false;
} else if (s.length() == 0) {// 匹配串为0
if (p.length() % 2 == 1)
return false;
else {
for (int i = 1; i < p.length(); i += 2) {
if (p.charAt(i) != '*')
return false;
}
return true;
}
} else if (p.length() == 1) {//匹配串长度为1
if((s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')&&s.length()==1)//可以匹配
return true;
else {
return false;
}
} else {// 两个串串正常长度
if(p.charAt(1)=='*')//下一个为*
{
if(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.')//可以匹配 分别用0次 用若1次 用若干次
{
return isMatch(s.substring(1), p)||isMatch(s.substring(1), p.substring(2))||isMatch(s, p.substring(2));
}
else {//不匹配只能用0次
return isMatch(s, p.substring(2));
}
}
else {
if(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.')
return isMatch(s.substring(1), p.substring(1));
else {//完全失败
return false;
}
}
}
很遗憾的超时了,不过在剑指offer是可以过的,主要遇到这种字符就会很麻烦:
isMatch("aaaaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*c")
因为这里面匹配中的a*任意一个都可以使用若干次导致递归种类太多爆栈。嘤嘤嘤。
动态规划
这题正确而大众的解法当然是动态规划了,我们知道动态规划重在动态的规划方程。并且当前结果是基于父结果的。这题刚好就可以使用动态规划来解答。
我们使用我们声明一个dp[][]=new boolean[匹配串长度+1][模式串长度+1] 的二位数组用来储存结果, 其中dp[i][j]表示匹配串前i个和模式串前j个是否匹配。最终匹配串和模式串是否匹配就是返回dp[匹配串长度][模式串长度].
对于动态规划的问题,我们一般会空余出0号位放在越界等特殊情况,所以我们声明的二维数组大小长宽都大1,因为0号在dp[][]表示的是空串的结果而不是一号位置串的结果。然后我们在搞动态规划题一般需要以下几步:
- 声明dp数组,理解其含义
- 声明一些初始情况(一般为0)
- 找正常情况动态方程式
这里的初始我们是dp[0][0]=true表示两个空串可以匹配。
我们分析这个dp[i][j] 匹配串前i个,模式串前j个是否匹配.其实这个分析和之前递归还是有点相似的:
首先如果模式串pattern第j个如果是*,以下两种情况任意一种匹配成功即可。
- 如果dp[i][j-2]==true那么dp[i][j]肯定为true,因为可以把它看成一个空串。
- 如果dp[i][j-2]不为true也不要紧,如果匹配串和模式串前一个字符可以匹配并且dp[i-1][j]为true,那么也可以匹配(a*和a )
如果模式串第j个不为*那么就是常规匹配了,如果当前位置字符不匹配,那么就为false,如果当前位置匹配且dp[i-1][j-1]==true那么dp[i][j]就为true:
当然,以上所有考虑i-1的情况i不能等于0.
综上分析得到dp方程为:
if(模式串当前为*)
dp[i][j]==dp[i][j-2]||(dp[i-1][j]&&两串当前字符可以匹配)
else
dp[i][j]=dp[i-1][j-1]&&两串当前字符可以匹配
具体实现需要注意下标编号在字符串位置和dp下标的含义,具体实现的代码为:
public static boolean isMatch(String s, String p) {
boolean dp[][]=new boolean[s.length()+1][p.length()+1];//默认为false
dp[0][0]=true;
for(int i=0;i<=s.length();i++)
{
for(int j=1;j<=p.length();j++)
{
if(p.charAt(j-1)=='*')//该位置为*
{
dp[i][j]=dp[i][j-2];//模式用了0次的看看是否能够匹配,能匹配最好,不能匹配继续
if(!dp[i][j])//不能匹配
{
if(i==0) {continue;}
else if(s.charAt(i-1)==p.charAt(j-2)||p.charAt(j-2)=='.')//可以匹配
{
dp[i][j]=dp[i-1][j];
}
}
}
else {//正常字符
if(i==0){continue;}
else if(s.charAt(i-1)==p.charAt(j-1)||p.charAt(j-1)=='.') {//这个位置可以匹配
dp[i][j]=dp[i-1][j-1];
}
}
}
}
return dp[s.length()][p.length()];
}
结语
今天又get一个动态规划题,以前没有用动态规划的思维去想过,但是这题还是挺好的,至于一些其他的方法如果后面有时间可以继续拓展。
好啦,如果感觉不错,欢迎点赞、关注、收藏一键三联!欢迎关注公众号:bigsai,私信回复打卡加入LeetCode打卡交流!
公众号:bigsai 原创头条号:码农bigsai
- 上一篇:JS 写正则表达式,判断是否为手机号
- 下一篇:推荐三款正则可视化工具「JS篇」
相关推荐
- 全网最详细解决Windows下Mysql数据库安装后忘记初始root 密码方法
-
一、准备重置root的初始化密码Win+R键启动命令输入窗口;输入cmd打开命令执行窗口;##界面如下##输入命令:netstopmysqld#此操作会停止当前运行的...
- Spring Boot数据库密码加密的配置方法
-
前言由于系统安全的考虑,配置文件中不能出现明文密码的问题,本文就给大家详细介绍下springboot配置数据库密码加密的方法,下面话不多说了,来一起看看详细的介绍吧...
- Mysql 8.4数据库安装、新建用户和数据库、表单
-
1、下载MySQL数据库yuminstall-ywgetperlnet-toolslibtirpc#安装wget和perl、net-tools、libtirpcwgethtt...
- mysql5.7安装教程
-
首先下载mysql的rpm包wgethttps://mirrors.ustc.edu.cn/mysql-ftp/Downloads/MySQL-5.7/mysql-community-client...
- MySQL管理授权和数据库的备份和还原详解
-
一般管理用户和授权由DBA去执行,DBA为数据库管理员一、管理用户1.添加用户...
- 数据库迁移有什么技巧?|分享强大的database迁移和同步工具
-
概述DBConvertStudio是一款强大的跨数据库迁移和同步软件,可在不同数据库格式之间转换数据库结构和数据。它将成熟、稳定、久经考验的DBConvert和DBSync核心与改进的现代...
- Mysql解压版安装过程
-
Mysql是目前软件开发中使用最多的关系型数据库,具体安装步骤如下:第一步:Mysql官网下载最新版(mysql解压版(mysql-5.7.17-winx64)),Mysql官方下载地址为:https...
- MySQL5.7升级到8.0过程详解
-
前言:不知不觉,MySQL8.0已经有好多个GA小版本了。目前互联网上也有很多关于MySQL8.0的内容了,MySQL8.0版本基本已到稳定期,相信很多小伙伴已经在接触8.0了。本篇文章主要介绍从5....
- 10种常见的MySQL错误,你可中招?
-
【51CTO.com快译】如果未能对MySQL8进行恰当的配置,您非但可能遇到无法顺利访问、或调用MySQL的窘境,而且还可能给真实的应用生产环境带来巨大的影响。本文列举了十种MySQL...
- 忘记MySQL密码怎么办?一招教你搞定
-
在安装完MySQL或者是在使用MySQL时,最尴尬的就是忘记密码了,墨菲定律也告诉我们,如果一件事有可能出错,那么它一定会出错。那如果我们不小心忘记了MySQL的密码,该如何处理呢?别着急...
- Windows 安装解压版本的 MySql
-
1、下载解压版本的MySql到https://downloads.mysql.com/archives/community/网站,根据自己需要安装的版本进行选择下载,这里下载不要选择MSII...
- 爆破SSH/MySQL账户竟如此简单
-
友情提示:初入安全,小白一个,本文重在学习与经验分享!背景使用Kali自带的MSF工具对SSH的账号密码进行爆破。1.实验环境本次实验通过MSF,可直接对SSH的账号密码进行爆破。KaliIP:1...
- Mysql8忘记密码/重置密码
-
Mysql8忘记密码/重置密码UBUNTU下Mysql8忘记密码/重置密码步骤如下:先说下大概步骤:修改配置文件,使得用空密码可以进入mysql。然后置当前root用户为空密码。再次修改配置文件,不能...
- wamp查看MySQL密码 MySQL console输入密码闪退 重置mysql密码
-
wampserver的MySQL数据库用户名为root初始密码为空,但是部分同学通过MySQLconsole访问数据库输入密码的时候出现窗口闪退,常见的问题是原来有改过密码或者你的配置文件要求密码不...
- Mysql数据库操作指引(六)——账号密码及权限管理
-
简介:在MySQL数据库中,为了保证数据的安全性,数据管理员需要根据需要创建账户,并为每个账户赋予不同的权限,以满足不同用户的需求。...
- 一周热门
- 最近发表
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- patch补丁 (31)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- dedecms模版 (53)
- c 视频教程下载 (33)
- listview排序 (33)
- firebug 使用 (31)
- characterencodingfilter (33)
- getmonth (34)
- hibernate教程 (31)
- label换行 (33)
- curlpost (31)
- android studio 3 0 (34)
- android应用开发 (31)
- html转js (35)
- 索引的作用 (33)
- checkedlistbox (34)
- localhost 8080 (32)
- 多态 (32)
- xmlhttp (35)
- mysql更改密码 (34)