一,题目简述
Given an input string (s
) and a pattern (p
), implement regular expression matching with support for '.'
and '*'
.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
字符串匹配问题:
给定字符串str,其中绝对不含有字符'.'和'*'。再给定字符串pattern,其中可以含有'.'或'*','*'字符不能是pattern的首字符,并且任意两个'*'字符不相邻。
pattern中的'.'代表任何一个字符
pattern中的'*'表示前一个字符可以有0或多个。
请写一个函数,判断str是否被pattern匹配。
二,题目分析
字符串匹配问题,题中给了两个特殊的模式符号'.'和'*',理解他们的含义然后暴力判断字符串就好。重点在于读题审题理解题意,然后多学多做。
三,题目解答
递归版:
class Solution {
public boolean isMatch(String s, String p) {
if(s == null || p == null)
return false;
char[] str = s.toCharArray();
char[] pat = p.toCharArray();
return isValid(str, pat) ? process(str, pat, 0, 0):false;
}
public boolean isValid(char[] s, char[] p){//判断输入是否有效
for(int i = 0; i < s.length; i++){
if(s[i] == '*' || s[i] == '.')
return false;
}
for(int i = 0; i < p.length; i++){
if(p[i] == '*' &&(i == 0 || p[i-1] == '*'))
return false;
}
return true;
}
public boolean process(char[] s, char[] p, int si, int pi){//判断是否匹配
if(pi == p.length)
return si == s.length;
if(pi +1 == p.length || p[pi+1] != '*'){
return si != s.length && (p[pi] == s[si] || p[pi] == '.') && process(s,p,si+1,pi+1);
}
while(si != s.length && (p[pi] == s[si] || p[pi] == '.')){
if(process(s,p,si,pi+2))
return true;
si++;
}
return process(s,p,si,pi+2);
}
}
首先,isValid函数判断输入是否有效,即字符串s不能包含'*'和'.',字符串p中'*'字符前不能为空或者字符'*'
判断函数process用递归判断,pi,si分别标识字符串p和s的所在位置,即判断字符串p的pi位置之后的字符串是否能模式识别字符串s的si位置之后的字符串。
如果p的pi+1位置不是'*'的话,只需要判断现在所处的字符是否匹配,并且再继续往下判断si+1和pi+1位置后的字符串。
如果p的pi+1位置是'*'的话,两种情况:一是'*'代表0次,我们判断p的pi+2位置之后和s的si位置之后即可;二是'*'代表非0次,我们保持pi不动,si向后移直到跳出循环即可。
非递归版
同理我们可以用深度搜索代替递归,额外空间复杂度一个布尔数组dp[i][j]表示字符串s的i位置之后部分和p的j位置之后的部分是否匹配。
class Solution {
public boolean isMatch(String s, String p) {
if(s == null || p == null)
return false;
char[] str = s.toCharArray();
char[] pat = p.toCharArray();
if(!isValid(str, pat))
return false;
boolean[][] dp = initDpMap(str, pat);
for(int i = str.length-1; i > -1; i--){
for(int j = pat.length-2; j > -1; j--){
if(pat[j+1] != '*'){
dp[i][j] = (str[i] == pat[j] || pat[j] == '.')&&dp[i+1][j+1];
}else{
int si = i;
while(si != str.length &&(str[si] == pat[j] || pat[j] == '.')){
if(dp[si][j+2]){
dp[i][j] = true;
break;
}
si++;
}
if(dp[i][j] != true){
dp[i][j] = dp[si][j+2];
}
}
}
}
return dp[0][0];
}
public boolean[][] initDpMap(char[] s, char[] p){
int slen = s.length;
int plen = p.length;
boolean[][] dp = new boolean[slen+1][plen+1];
dp[slen][plen] = true;
for(int i = plen-2; i > -1; i--){
if(p[i] != '*' && p[i+1] == '*'){
dp[slen][i] = true;
}else
break;
}
if(slen > 0 && plen > 0){
if(p[plen-1] == '.' || s[slen-1] == p[plen-1]){
dp[slen-1][plen-1] = true;
}
}
return dp;
}
public boolean isValid(char[] s, char[] p){//判断输入是否有效
for(int i = 0; i < s.length; i++){
if(s[i] == '*' || s[i] == '.')
return false;
}
for(int i = 0; i < p.length; i++){
if(p[i] == '*' &&(i == 0 || p[i-1] == '*'))
return false;
}
return true;
}
}
四,题目回忆
字符串问题的两大处理情景,带字符串下标的递归和借助布尔数组的dp。可以归纳出字符串处理的问题。