题意:
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true
设左边字符串为s,右边字符串为p。判断是否可以用右边的字符串完全匹配左边的字符串。
其中‘.’可以代表任何一个单个字符,‘*’代表其之前的字符有任意个(例如"c*"可以代表 没有c,1个c,2个c...)
倒数第二个样例解释: “.*”用来代表“..”,“..”可以完全匹配“ab”
倒数第一个样例解释:“c*a*b”中“c*”代表空字符(0个c),“a*”代表“aa”
解决方法:
1.暴力深搜
刚开始我们需要解决的问题是:
判断s和p从各自的起始位置s[0] p[0]开始,直到他们的结束位置s[s.length()-1],p[p.length()-1]为止是否能完全匹配。
假设现在已经匹配到了s[i],p[j](二者之前的子串完全匹配),现在问题转化成:
判断s和p从各自的起始位置s[i] p[j]开始,直到他们的结束位置s[s.length()-1],p[p.length()-1]为止是否能完全匹配。
以上是状态转化。
由于p中字符"*" "."的存在,要考虑到的问题多了一些:
(1)当前状态起始位置为s[i] p[j],p[j+1] != '*':若s[i] == p[j], 状态转移到起始位置为s[i+1] p[j+1];若s[i] != p[j],返回false。
(2)当前状态起始位置为s[i] p[j],p[j+1] == '*':
因为*的特殊功能,所以p[j]这个字符,可以当作空字符(不存在),这样状态就转换为:状态起始位置为s[i] p[j+2]
可以当作1个字符:若s[i] == p[j], 状态转移到起始位置为s[i+1] p[j+2];
可以当作2个字符:若s[i] == p[j]且s[i+1] == p[j], 状态转移到起始位置为s[i+2] p[j+2];
可以当作3个字符:若s[i] == p[j]且s[i+1] == p[j]且s[i+2] == p[j], 状态转移到起始位置为s[i+3] p[j+2];
。。。。。。
代码:
class Solution {
public:
bool dfs(string s, string p, int i, int j){
if(j==p.length())
return i==s.length();
//p[j+1] != '*';
if(j==p.length()-1 || p[j+1]!='*')
{
if(i==s.length()|| s[i]!=p[j] && p[j]!='.')
return false;
else
return dfs(s,p,i+1,j+1);
}
//p[j+1]=='*'
while(i<s.length() && (p[j]=='.' || s[i]==p[j]))
{
if(dfs(s,p,i,j+2))
return true;
i++;
}
return dfs(s,p,i,j+2);
}
public:
bool isMatch(string s, string p) {
return dfs(s, p, 0, 0);
}
};
class Solution {
bool vis[1005][1005];
bool res[1005][1005];
public:
bool dfs(string s, string p, int i, int j){
if(vis[i][j]) return res[i][j];
vis[i][j] = true;
if(j==p.length())
return res[i][j] = (i==s.length());
if(j==p.length()-1 || p[j+1]!='*')
{
if(i==s.length()|| s[i]!=p[j] && p[j]!='.'){
return res[i][j] = false;
}
else{
return res[i][j] = dfs(s,p,i+1,j+1);
}
}
//p.charAt(j+1)=='*'
while(i<s.length() && (p[j]=='.' || s[i]==p[j]))
{
if(dfs(s,p,i,j+2)){
return res[i][j] = true;
}
i++;
}
return res[i][j] = dfs(s,p,i,j+2);
}
public:
bool isMatch(string s, string p) {
//memset(vis, false, sizeof(vis));
return dfs(s, p, 0, 0);
}
};
2.动态规划
/**
* f[i][j]: if s[0..i-1] matches p[0..j-1]
* if p[j - 1] != '*'
* f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
* if p[j - 1] == '*', denote p[j - 2] with x
* f[i][j] is true iff any of the following is true
* 1) "x*" repeats 0 time and matches empty: f[i][j - 2]
* 2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
* '.' matches any single character
*/
这是我看到的一个思路,感觉特别好。
f[i][j]状态定义为s的前i 个字符 与 p的前j个字符是否完全匹配:true/false
(1)p[j-1] != '*'时,直接根据f[i-1][j-1]和s[i-1] p[j-1]转移状态
if p[j - 1] != '*'
f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
(2)p[j-1] == '*'时,
p[j-2]可以当作空字符,此时f[i][j]状态由f[i][j-2]状态直接得到;
p[j-1]可以当作>=1个字符:此时若s[i-1] == p[j-2]只需要f[i-1][j]为true,s[i-1]这个字符就可以用‘*’重复出来的p[j-2]来匹配(而不用管之前这个p[j-1]已经重复造出来过几个p[j-2])
代码:为了加深自己的理解,自己用java写了一下。
public class Solution {
public static boolean isMatch(String s, String p) {
boolean[][] f = new boolean[s.length()+1][p.length()+1];
f[0][0] = true;
boolean tmp = true;
for(int j = 1; j <= p.length(); j++){
if(tmp && p.charAt(j-1) != '*' && j < p.length() && p.charAt(j) != '*') tmp = false;
if(tmp && j == p.length() && p.charAt(j-1) != '*') tmp = false;
f[0][j] = tmp;
}
for(int i = 1; i <= s.length(); i++){
for(int j = 1; j <= p.length(); j++){
if(p.charAt(j-1) != '*'){
f[i][j] = f[i-1][j-1] && (s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '.');
}
else{
f[i][j] = f[i][j-2] || ((s.charAt(i-1) == p.charAt(j-2) || p.charAt(j-2) == '.') && f[i-1][j]);
}
}
}
//System.out.println(f[1][2]);
return f[s.length()][p.length()];
}
}
希望对大家有所帮助,欢迎大家将任何问题在回复区讨论。