剑指offer:正则表达式匹配

剑指offer:正则表达式匹配

题意描述

请实现一个函数用来匹配包括'.'和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配

解题思路

一、思路一

  1. 两个字符串都为空,返回true
  2. 当第一个字符串不空,而第二个字符串空了,返回false(因为这样,就无法匹配成功了,而如果第一个字符串空了,第二个字符串非空,还是可能匹配成功的,比如第二个字符串是“aaaa”,由于‘ * ’之前的元素可以出现0次,所以有可能匹配成功)
  3. 开始匹配第一个字符,这里有两种可能:匹配成功或匹配失败。但考虑到pattern下一个字符可能是‘ * ’, 这里我们分两种情况讨论:pattern下一个字符为‘ * ’或不为‘ * ’:
    • pattern下一个字符不为‘ * ’:
      • 如果str第一个字符和ptr中的第一个字符相匹配,那么str和ptr都后移一个字符,然后匹配剩余的。
      • 如果str第一个字符和ptr中的第一个字符相不匹配,直接返回false。
    • pattern下一个字符为‘ * ’时,稍微复杂一些,因为‘ * ’可以代表0个或多个。这里把这些情况都考虑到:
      • x* 匹配0个字符,str不变,ptr后移两位,跳过这个’ * ‘
      • x* 匹配1个字符时,str+1,ptr+2,匹配str下个字符,并且跳过’ * ‘
      • x* 匹配多个字符时,str+1,ptr不变,继续匹配str下个字符
      • 匹配一个也可以看成匹配多个
    public class Solution {
        public boolean match(char[] str, char[] pattern){
            if(str == null && pattern == null) return true;	//str、pattern都为空,返回true
            if(str != null && pattern == null) return false;//str不为空,pattern为空,匹配失败
            return matchCore(str,0,pattern,0);
        }
        public boolean matchCore(char[] str, int s1, char[] ptr, int p1) {
            //str匹配到尾部,ptr匹配到尾部,匹配成功
            if(s1 == str.length && p1 == ptr.length){
                return true;
            }
            //str没有匹配到尾部,ptr匹配到尾部,匹配失败
            if(s1 != str.length && p1 == ptr.length){
                return false;
            }
            //p1的下一个字符为‘*’,并且没有越界
            if(p1+1<ptr.length && ptr[p1 + 1] == '*'){
                //如果s1没有匹配到尾部,p1 == s1 或者p1 = ‘。’
                if((s1 != str.length && ptr[p1] == str[s1]) ||(ptr[p1] == '.' && s1 != str.length)){
                    return matchCore(str,s1,ptr,p1+2) ||	//X*匹配0个字符
                            //matchCore(str,s1+1,ptr,p1+2)||	//X*匹配1个字符
                            matchCore(str,s1+1,ptr,p1);		//X*匹配多个字符,继续匹配s1下个字符
                }else{
                    //s1 != p1 并且 p1 != ‘。’,匹配不成功,跳过‘ * ’
                    return matchCore(str,s1,ptr,p1 + 2);
                }
            }
            //str没有匹配到尾部,p1 == s1 或者 p1 = ‘。’,但p1下一位不是‘*’
            if((s1 != str.length && ptr[p1] == str[s1])||(ptr[p1] == '.' && s1 != str.length)){
                //匹配s1下一位与p1下一位
                return matchCore(str,s1 + 1,ptr,p1 + 1);
            }
            return false;
        }
    }

二、思路二

  1. 使用动态规划,DP分为正向与反向,这里使用那种呢?
  2. 根据前面matchCore(str,s1 + 1,ptr,p1 + 1),相当于dp[ i ] [ j ] = dp[ i + 1 ] [ j + 1 ],所以我么采用反向
  3. 初始化boolean dp[ len1 + 1 ] [ len2 + 1 ],其中len1=str.length,len2=pattern.length
  4. 初始化dp[ len1 ] [ len2 ] = true,含义是从两个字符串的末位开始匹配,“”与“”一定为true
  5. 循环
    • 外循环:因为我们要用aa*匹配aaa,以aaa为外循环,注意,从""开始匹配接下来a,aa,aaa
    • 内循环:拿aa* 匹配:匹配顺序 * ,a* ,aa*
    public class Solution {
        public boolean match(char[] str, char[] pattern){
            if(str == null || pattern == null) return true;
            boolean[][] dp = new boolean[str.length+1][pattern.length+1];
            dp[str.length][pattern.length] = true; //末尾“”与“”一定匹配
            for(int i=str.length;i>=0;i--){	//从空串开始匹配
                for(int j=pattern.length-1;j>=0;j--){//从最后一个字符开始匹配
                    if(j+1<pattern.length && pattern[j+1] == '*'){	//下一位是*,并且没有越界
                        //str = ptr 或者 ptr+1 = 。
                        if(i < str.length && (str[i] == pattern[j] || pattern[j] == '.')){
                            dp[i][j] = dp[i][j+2] || dp[i + 1][j]; //匹配0个、1个或多个
                        }else{
                            dp[i][j] = dp[i][j+2];	//跳过*
                        }
                    }else{
                        //下一位不是*,并且str = ptr || ptr+1 = 。
                        if(i != str.length &&(str[i] == pattern[j] || pattern[j] == '.')){
                            dp[i][j] = dp[i+1][j+1];	//匹配下一位
                        }
                    }
                }
            }
            return dp[0][0];
        }
    }
posted @ 2020-05-10 18:47  灵图  阅读(508)  评论(0)    收藏  举报