通配符匹配
问题描述:
给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。
示例 3:
输入:
s = "cb"
p = "?a"
输出: false
解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
示例 4:
输入:
s = "adceb"
p = "*a*b"
输出: true
解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
示例 5:
输入:
s = "acdcb"
p = "a*c?b"
输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/wildcard-matching
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
递归
首先定义fun(i,j)返回s从 i 开始,p从 j 开始能否匹配的上。
对当前元素s[i] 和 p[j] 分情况讨论,
若s[i] 恰好等于p[j],或者p[j]为 ‘?’时,fun(i, j)的求解就可以转化为对 fun(i + 1, j + 1)的求解;
若p[i]即不为’?’ ‘*’这些,又不等于s[i],s从i开始p从j开始无论如何也匹配不上;
剩下就只有p[j] 为 ’*‘这种可能了,依次让p[j] 匹配0个,1个,2个….k个,fun(i, j)的求解就可以转化为求解一系列的fun(i + k, j + 1)的值。
递归的出口:
当p已经没了,但是s还有,该情况无论如何也匹配不上了,返回false;
当p和s都没了 返回true;
当s没了p还有,该情况下只有p的后续全为’*’才能匹配上。
实现代码如下:
class Solution {
public boolean isMatch(String s, String p) {
return process(s.toCharArray(), p.toCharArray(), 0, 0);
}
public boolean process(char[] s, char[] p, int i, int j){
if(j == p.length){
return i == s.length;
}
if(i == s.length){
return p[j] == '*' && process(s, p, i, j + 1);
}
if(p[j] == '?' || p[j] == s[i]){
return process(s, p, i + 1, j + 1);
}
if(p[j] != '*'){
return false;
}
// p[j] == '*';
for(int k = 0; i + k <= s.length; k++){
if(process(s, p, i + k, j + 1)){
return true;
}
}
return false;
}
}
动态规划
将上述递归解法改成动态规划,
定义dp 为(M + 1) * (N + 1)的布尔型数组,dp[i] [j] 为s从 i 开始,p从 j 开始能否匹配上,其中M为s的长度,N为p的长度。
转移函数:
baseline:
实现代码如下:
class Solution {
public boolean isMatch(String s, String p) {
char[] arrS = s.toCharArray();
char[] arrP = p.toCharArray();
int M = arrS.length;
int N = arrP.length;
// dp[i][j] s以i开头, p 以j开头能否匹配上
boolean[][] dp = new boolean[M + 1][N + 1];
dp[M][N] = true;
for(int j = N - 1; j >= 0; j--){
if(arrP[j] == '*'){
dp[M][j] = true;
}else{
break;
}
}
for(int i = M - 1; i >= 0; i--){
for(int j = N - 1; j >= 0; j--){
if(arrP[j] == '?' || arrS[i] == arrP[j]){
dp[i][j] = dp[i + 1][j + 1];
}else if(arrP[j] == '*'){
for(int k = 0; i + k <= M; k++){
if(dp[i + k][j + 1]){
dp[i][j] = true;
break;
}
}
}
}
}
return dp[0][0];
}
}
该算法的时间复杂度为O(N* M ^2)。
优化后的DP
我们发现求解dp[i] [j]当p[j]为’*’时,需要从当前行(i行)开始对右边一列(dp[i] [j + 1] … dp[M] [j + 1])进行或运算,我们发现dp[i + 1] [j]的求解也是从当前行(i + 1行)为从dp[i + 1 ] [j + 1] … dp[M] [j + 1],因此优化后的转移函数如下:
实现代码如下:
public boolean isMatch(String s, String p) {
char[] arrS = s.toCharArray();
char[] arrP = p.toCharArray();
int M = arrS.length;
int N = arrP.length;
// dp[i][j] s以i开头, p 以j开头能否匹配上
boolean[][] dp = new boolean[M + 1][N + 1];
dp[M][N] = true;
for(int j = N - 1; j >= 0; j--){
if(arrP[j] == '*'){
dp[M][j] = true;
}else{
break;
}
}
for(int i = M - 1; i >= 0; i--){
for(int j = N - 1; j >= 0; j--){
if(arrP[j] == '?' || arrS[i] == arrP[j]){
dp[i][j] = dp[i + 1][j + 1];
}else if(arrP[j] == '*'){
dp[i][j] = dp[i + 1][j] || dp[i][j + 1];
}
}
}
return dp[0][0];
}
时间复杂度为O(N * M)。
- 基于 Ubuntu 搭建 VNC 远程桌面服务
- 搭建 FTP 文件服务
- linux平台下的写文件刷新
- MySQL NDB Cluster实战
- 译文 | 简明 TensorFlow 教程:混合模型
- 利用SetConsoleTextAttribute函数设置控制台颜色
- python笔记:#001#python简介
- python笔记:#002#第一个python程序
- python笔记:#003#PyCharm 的初始设置
- python笔记:#004#注释
- python笔记:#005#算数运算符
- python笔记:#006#程序执行原理
- python笔记:#007#变量
- python笔记:#008#变量的命名
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- alpine安装sshd/ssh server
- 使用jstack检测Java应用的死锁(deadlock)状态
- 使用xdebug对php做性能分析调优
- 使用SAP Analytics Path Framework通过图表和表格方式展示CDS view数据
- ELK学习笔记之Docker Container exited with code 137
- 用这10个小技巧加速Python编程
- Dijkstra算法及其C++实现
- WPF 启动屏幕键盘
- dotnet 里的那些锁 AutoResetEvent 用法
- MySQL是如何实现事务的ACID
- 白话K8S核心组件概念
- k8s 架构、基本概念及命令
- Java API 连接 Hbase示例
- 点线图和阶梯图的画法
- 添加直线的两种方式