LeetCode题目:44. Wildcard Matching
原题链接:https://leetcode.com/problems/wildcard-matching/description/
解题思路:
核心思想:
由题可知,’*’意思是匹配认可,因此,多个连续的’*’组成的字符串和单个’*’的意义一样,因此,可以把输入子串s根据’*’分割,变成字符串数组ss.
字符串数组分为三部分:
- 首位字符串ss[0]:如果输入字符串不以’*’开头,则首位字符串必须要与母串的开头匹配.
- 末尾字符串ss[length-1]:如果输入字符串不以’*’结束,则末尾字符串必须要与母串的末尾匹配.
- 其他字符串:其他字符串只要母串的子串即可,但是必须要按照顺序,即ss[1]的匹配位置必须要比ss[2]的匹配位置小.
因此,该题在字符串分割成字符串数组,并先将首位字符串和末尾字符串的特殊情况解决后,就变成了一个贪心问题,(很明显,对每一个数组中的字符串,匹配的位置最前,匹配成功的可能性就越大)因此,我们可以用贪心算法解决.
步骤如下:
将输入子串s根据’*’分割成字符串数组ss
若s[0]不为’*’,将ss[0]与母串p的开头匹配,若匹配成功,则在数组中移除ss[0],p移除相应部分
若s[length-1]不为’*’,将ss[length-1]与母串p的结尾匹配,若匹配成功,则在数组中移除ss[length-1],p移除相应部分
i从0->n-1遍历数组,将字符串ss[i]与p匹配,并找到第一个匹配位置:
如果匹配成功,p移除匹配部分和匹配之前的部分,只留下匹配之后的部分
如果字符串数组中的全部字符串都匹配成功,则返回true,否则返回false
代码细节:
- c++中并没有split函数,需要自己去实现
- 字符串匹配时,需要注意’?’可以代替任何字符
坑点:
- 此题和第10题Regular Expression Matching类似.但是由于此题数据量大,用递归的方法会超出时间,因此改用迭代.
- 需要注意”“和”*”.
- 若子串没有包含’*’,则就是正常匹配.
代码:
#include <cstring>
#include <string>
bool isEqual(char a, char b) {
return a == b || b == '?' || a == '?';
}
bool isEqual(string a, string b) {
if (a.length() != b.length()) return false;
for (int i = 0; i < a.length(); i ++)
if (!isEqual(a[i], b[i])) return false;
return true;
}
int subStr(string s, string b, int pos = 0) {
for (int j = pos; j <= int(s.size() - b.size()); j++) {
int k;
for (k = 0; k < b.length(); k++)
if (!isEqual(b[k], s[j + k])) break;
if (k == b.length()) return j;
}
return -1;
}
// 切割字符串
vector<string> split(string& str,const char* c)
{
char *cstr, *p;
vector<string> res;
cstr = new char[str.size()+1];
strcpy(cstr,str.c_str());
p = strtok(cstr,c);
while(p!=NULL)
{
res.push_back(p);
p = strtok(NULL,c);
}
return res;
}
bool isMatch(string s, string p) {
// 没有*的情况
if (p.find('*') == -1) return isEqual(s, p);
vector<string> splitStrs = split(p, "*"); // 将字符串根据*分割
// 第一个固定,没有*
if (p[0] != '*') {
string firstStr = splitStrs.front();
splitStrs.erase(splitStrs.begin());
// 判断头部是否相等
if (s.length() < firstStr.length() ||
!isEqual(s.substr(0, firstStr.length()), firstStr)) return false;
s.erase(0, firstStr.length());
}
// 最后一个固定,没有*
if (p[p.length() - 1] != '*') {
string endStr = splitStrs.back();
splitStrs.pop_back();
// 判断尾部是否相等
if (s.length() < endStr.length() ||
!isEqual(s.substr(s.length() - endStr.length(), endStr.length()), endStr)) return false;
s.erase(s.length() - endStr.length(), endStr.length());
}
int pos = 0;
for (int i = 0; i < splitStrs.size(); i++) {
// 可由贪心,寻找第一个子串
pos = subStr(s, splitStrs[i], pos);
// 如果之后不存在子串匹配,直接退出
if (pos == -1) return false;
pos += splitStrs[i].length();
}
return true;
}