Easy
题目描述
给你一个字符串 s 和一个模式字符串 p,其中 p 恰好包含一个 '*' 字符。
p 中的 '*' 可以替换为零个或多个字符的任意序列。
如果 p 可以成为 s 的子串,返回 true,否则返回 false。
示例 1:
输入:s = "leetcode", p = "ee*e"
输出:true
解释:通过将 '*' 替换为 "tcod",子串 "eetcode" 匹配模式。
示例 2:
输入:s = "car", p = "c*v"
输出:false
解释:没有子串匹配该模式。
示例 3:
输入:s = "luck", p = "u*"
输出:true
解释:子串 "u"、"uc" 和 "uck" 都匹配该模式。
约束条件:
1 <= s.length <= 501 <= p.length <= 50s仅包含小写英文字母p仅包含小写英文字母和恰好一个'*'
提示:
- 将模式分成两个字符串,在原字符串中搜索。
解题思路
这道题的核心思路是将包含通配符 * 的模式字符串分割成两部分:* 前面的前缀和 * 后面的后缀。
解法分析:
字符串分割法(推荐):找到
*的位置,将模式字符串分成前缀和后缀两部分。然后在原字符串s中寻找所有可能的匹配位置。暴力匹配法:枚举
s的所有子串,检查是否能通过某种*的替换方式匹配模式。
具体实现思路:
对于分割法,我们需要:
- 找到模式字符串中
*的位置 - 获取前缀(
*之前的部分)和后缀(*之后的部分) - 在字符串
s中寻找前缀的所有出现位置 - 对于每个前缀位置,检查从该位置开始是否能找到对应的后缀
- 关键点:前缀结束位置到后缀开始位置之间可以有任意字符(包括空字符串)
这种方法的优势是逻辑清晰,易于理解和实现。时间复杂度在最坏情况下为 O(n²),其中 n 是字符串长度。
代码实现
class Solution {
public:
bool hasMatch(string s, string p) {
int star_pos = p.find('*');
string prefix = p.substr(0, star_pos);
string suffix = p.substr(star_pos + 1);
int n = s.length();
int prefix_len = prefix.length();
int suffix_len = suffix.length();
// 枚举前缀在s中的所有可能位置
for (int i = 0; i <= n - prefix_len; i++) {
// 检查前缀是否匹配
if (s.substr(i, prefix_len) == prefix) {
// 枚举后缀在s中的所有可能位置
for (int j = i + prefix_len; j <= n - suffix_len; j++) {
// 检查后缀是否匹配
if (s.substr(j, suffix_len) == suffix) {
return true;
}
}
}
}
return false;
}
};
class Solution:
def hasMatch(self, s: str, p: str) -> bool:
star_pos = p.index('*')
prefix = p[:star_pos]
suffix = p[star_pos + 1:]
n = len(s)
prefix_len = len(prefix)
suffix_len = len(suffix)
# 枚举前缀在s中的所有可能位置
for i in range(n - prefix_len + 1):
# 检查前缀是否匹配
if s[i:i + prefix_len] == prefix:
# 枚举后缀在s中的所有可能位置
for j in range(i + prefix_len, n - suffix_len + 1):
# 检查后缀是否匹配
if s[j:j + suffix_len] == suffix:
return True
return False
public class Solution {
public bool HasMatch(string s, string p) {
int starPos = p.IndexOf('*');
string prefix = p.Substring(0, starPos);
string suffix = p.Substring(starPos + 1);
int n = s.Length;
int prefixLen = prefix.Length;
int suffixLen = suffix.Length;
// 枚举前缀在s中的所有可能位置
for (int i = 0; i <= n - prefixLen; i++) {
// 检查前缀是否匹配
if (s.Substring(i, prefixLen) == prefix) {
// 枚举后缀在s中的所有可能位置
for (int j = i + prefixLen; j <= n - suffixLen; j++) {
// 检查后缀是否匹配
if (s.Substring(j, suffixLen) == suffix) {
return true;
}
}
}
}
return false;
}
}
/**
* @param {string} s
* @param {string} p
* @return {boolean}
*/
var hasMatch = function(s, p) {
const starIndex = p.indexOf('*');
const prefix = p.substring(0, starIndex);
const suffix = p.substring(starIndex + 1);
for (let i = 0; i <= s.length - prefix.length - suffix.length; i++) {
if (s.substring(i, i + prefix.length) === prefix) {
for (let j = i + prefix.length; j <= s.length - suffix.length; j++) {
if (s.substring(j, j + suffix.length) === suffix) {
return true;
}
}
}
}
return false;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 其中 n 是字符串 s 的长度。外层循环枚举前缀位置,内层循环枚举后缀位置 |
| 空间复杂度 | O(1) | 只使用了常数额外空间存储前缀、后缀和几个变量 |
相关题目
- . Wildcard Matching (Hard)