题目:
预备知识:
(1)正则表达式的概念是对字符串操作的逻辑公式,是事前定义好的一些特定的字符以及特定字符的组合。这里题目中的p就是正则表达式(字符模式)要判断s是否满足p的字符模式。正则表达式是描述了一种字符串匹配的模式,用来检查一个串中是否含有某种模式的子串,或者将匹配的子串替代又或者从里面取出符合某种模式的子串。所以并不是简单的看是否是子串的问题,例如:s="aaa" p="aaaaa"和s="aaa" p="aaaab"就不匹配,若p中没有特殊字符'*' 和 '.',只有当s="XX" p="XX"时候才匹配。
(2)关于特殊字符就是有某些特殊含义或者限制的字符,而且每个特殊字符都有自己的描述。
- 比如题目中的限定字符*表示匹配前面的字符0次或者多次,而且根据题目的描述*前面是需要跟着一个字符或者跟在‘.’后面,假设ab*,那么可以改表达式可以匹配a,b,ab,abbb,abbb(重复b).... 所以其中的b*可以看成是0也可以看出是n个b。但是假如abc*的时候,a,b均不匹配,此时匹配的字符串可以为“ab”“c”"abccc...."所以匹配的时候要看X*与字符串中的X的关系,又或者将X*看成0的话,看剩下的字符是否完全与被匹配的字符串完全相同(匹配)。例如s="abzh" p="abc*u*z*h",其中若将c* u* 匹配成0,z*与一个z匹配的时候,s就符合了p的字符模式。又或者:runnoo*b 不可以和runnb匹配,但是可以匹配runnob,runooob,runoooob....(就是要么0次要么一次或者多次)
- 题目中的‘.’就是可以匹配除去'\n''\r'之外的任一一个字符(只能为一个)所以题目中的p=".*"就表示表示0个或多个任意字符(.)
(3)所以综(1)(2)可以确定在这个题目中*一定要和它之前的一个字母或者 '.' 一起出现
暴力求解思路:
从*的匹配规则来看,很难处理的就是*,因为X*代表了很多种情况。将每一种X*的情况都去和s进行匹配。所以关键点就是先找到*然后再找到*之前的那个字符。若果是先找到字符的话,我们又要判断字符后面是否有*符号。所以选择的话就是从字符串的最后一个元素往前进行操作。所以用了j = p.size()-1, i = s.size()-1进行操作。
- 当p[j]=='*'的时候,它前面肯定有一个字符,此时只需要比较p[j-1]?=s[i]若相等,然后j不移动,再将i进行往前移动,看是否匹配,而当不匹配的时候,我们就需要回到不匹配时候的状态,说明此时p[j-1]p[j](该字符模式X*)和s不再匹配,此时需要移动到下一个位置(即j=j-2)的位置,然后再看这个位置是否p[j]?='*'然后再与此时的i进行比较。
- 当p[j]!=*的时候,说明p[j]此时是一个普通的字符,只需要与s[i]进行比较,看是否相同。若相同的话,将j,i都向前移动一个位置,然后再进行匹配,看是否符合字符串模式。若不相同的话,说明该次的匹配失败,状态置为false。所以其实从最后一个字符看的话,若p的最后一个字符不为*也不为 '.' ,而是其他普通字符,并且该字符不等于s[i]的话说明是肯定不匹配的。但是p的最后一个字符是'.',那么它肯定和所有普通字符都匹配的。
- 之前两条提到了若局部不匹配的话,需要回到上一个状态,此时就说明代码的框架需要一个递归(即回溯)去实现。因此有了递归,就需要考虑终止的条件。由于我们都是从p,s的末尾开始操作的,所以而且对于i的话,每次就是递减1,而j的话就是减去2,并且我们是围绕着p去匹配s的。(1)当j==-1的时候,有两种情况:要么i也为-1了,此时说明s已经成功匹配结束,但是当i没有为-1的话,而此时p已经结束了,说明s还有剩下的字符不能够匹配了,所以此时p肯定和s不匹配的,因为p不能完全覆盖s。(2)当i==-1的时候,有两种情况:要么j也为-1了,此时说明s和p都同时匹配成功并且结束了。若j!=-1的话,说明s匹配结束了,但是p还没有匹配结束,p字符串剩下的部分(不确定是否还有* 比如当s="ab"p="c*a*bb*h*z*"的时候)不能确定是否到底和s是否匹配,所以此时,还需要将j-2继续去判断和s进行比较。(此处因为有回溯的缘故,所以i 在此层可以小于0,所以肯定和此时p[j]不匹配,此时就进行回溯,一直回到i!=0那层去)所以所有的操作首要要以p的结束为结束去判断是否匹配成功,不能以s的结束为结束。
代码实现:
class Solution {
public:
bool isMatch(string s, string p) {
int i = s.size()-1;
int j = p.size()-1;
return mymatch(s,i,p,j);
}
bool mymatch(string s, int i, string p, int j)
{
if(j+1==0)
{
if(i+1==0)
{
return true;
}
else{
return false;
}
}
if(p[j]=='*')
{
if(i>=0 && (p[j-1]==s[i]||p[j-1]=='.'))
{
if(mymatch(s,i-1,p,j))//继续尝试匹配s的下一个字符,不匹配则返回到不匹配的目前状态
return true;
}
//无论当前的p[j-1]和s[i]是否相等,只要接下来匹配不了,i不变,就移动p的末尾指针继续与s匹配,跳到下一个j-2处(跳过*和*前的那个字符)
return mymatch(s, i, p, j-2);
}
//若当前p[j]!=*,那么直接比较p[j] s[i]即可,若不相等直接返回false
else{
if(i>=0&&(p[j]=='.'|| p[j]==s[i]))
{
//若相等的话,i,j一起往前移动一个位置,继续匹配
return mymatch(s,i-1,p,j-1);
}
return false;
}
}
};
刚刚开始不懂那个匹配到底是什么意思,所以就感觉连题目都没有理解到。后来看了下正则表达式的一些相关知识才差不多理解题目的意思。然后又看了《leetBook (leetCode详细解)》的解题思路和代码以后才大概理解以上几点东西吧。。。
链接:点击打开链接