• [LeetCode] 10. Regular Expression Matching ☆☆☆☆☆


    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).
    
    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

    解法1: 

      这道题中的*表示*之前的那个字符可以有0个,1个或是多个,就是说,字符串a*b,可以表示b或是aaab,即a的个数任意;字符串.*b,可以表示b或是xyzb。需要用递归Recursion来解,大概思路如下:(原字符串为s,正则式为p)

      - 若p为空:

      • 若s也为空,返回true,反之返回false

      - 若p的长度为1:

      • 若s长度也为1,且相同或是p为'.'则返回true,反之返回false

      - 若p的第二个字符不为*:

      • 若此时s为空返回false,否则判断首字符是否匹配,且从各自的第二个字符开始调用递归函数匹配

      - 若p的第二个字符为*:

      • 若s不为空且字符匹配,调用递归函数匹配s和去掉前两个字符的p,若匹配返回true,否则s去掉首字母
      • 否则,返回调用递归函数匹配s和去掉前两个字符的p的结果
    public class Solution {
        public boolean isMatch(String s, String p) {
            if (p.isEmpty()) {
                if (s.isEmpty()) return true;
                return false;
            }
            
            if (p.length() == 1)
                return (s.length() == 1 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'));
            
            if (p.charAt(1) != '*')
                return (s.length() > 0 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.') && isMatch(s.substring(1), p.substring(1)));
                
            while (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')) {
                if (isMatch(s, p.substring(2))) return true;
                s = s.substring(1);
            }
            return isMatch(s, p.substring(2));
        }
    }

      上面的方法可以写的更加简洁一些,但是整个思路还是一样的,我们先来判断p是否为空,若为空则根据s的为空的情况返回结果。当p的第二个字符为*号时,由于*号前面的字符的个数可以任意,可以为0,那么我们先用递归来调用为0的情况,就是直接把这两个字符去掉再比较,或者当s不为空,且第一个字符和p的第一个字符相同时,我们再对去掉首字符的s和p调用递归,注意p不能去掉首字符,因为*号前面的字符可以有无限个;如果第二个字符不为*号,那么我们就老老实实的比较第一个字符,然后对后面的字符串调用递归,参见代码如下:

    public class Solution {
        public boolean isMatch(String s, String p) {
            if (p.isEmpty()) {
                if (s.isEmpty()) return true;
                return false;
            }
            
            if (p.length() > 1 && p.charAt(1) == '*')
                return (isMatch(s, p.substring(2)) || (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.') && isMatch(s.substring(1), p)));
            
            return (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.') && isMatch(s.substring(1), p.substring(1)));
        }
    }

    解法2: 

      用动态规划DP来解,用二维数组 dp[i][j] 表示s的前i个字符s[0,i) 和p的前j个字符p[0,j)是否匹配,分以下几种情况:

    • 当前字符 p[j-1]=='*' 时,*前的字符可能不出现或者至少出现一次,即:
    dp[i][j] = dp[i][j - 2] || (i > 0 && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') && dp[i - 1][j]);
      • dp[i][j - 2] 表示*前的字符一次都不出现,即与 s[0,i)和p[0,j-2) 的匹配情况相同;
      • i > 0 && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') && dp[i - 1][j] 表示*前的字符至少出现一次,即与 s[0,i-1)和p[0,j)de匹配情况相同
    • 当前字符 p[j-1]!='*' 时,当前字符 s[i-1] 和 p[j-1]必须匹配,同时之前的字符串 s[0,i-1)和p[0,j-1)必须匹配, s[0,i)和p[0,j)才能匹配
    dp[i][j] = i > 0 && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') && dp[i - 1][j - 1]

    整体代码如下:

    public class Solution {
        public boolean isMatch(String s, String p) {
            boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
            dp[0][0] = true;
            
            for (int i = 0; i <= s.length(); i++) {
                for (int j = 1; j <= p.length(); j++) {  // j从1开始,因为i>0,j=0的情况肯定不匹配
                    if (j > 1 && p.charAt(j - 1) == '*') {
                        dp[i][j] = dp[i][j - 2] || (i > 0 && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') && dp[i - 1][j]);  // dp[i][j-2]表示*前的字符匹配0次,后面的表示匹配1次以上
                    } else {
                        // 不为*的时候,需要当前两个字符匹配,同时dp[i-1][j-1]
                        dp[i][j] = i > 0 && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') && dp[i - 1][j - 1];
                    }
                }
            }
            return dp[s.length()][p.length()];
        }
    }
  • 相关阅读:
    OC3(字符串,值类)
    OC2(初始化方法)
    OC1(类和对象)
    postgresql 时间戳格式为5分钟、15分钟
    centos添加ftp用户并禁止外切目录
    postgresql 判断日期是否合法
    tigerVNC的简单使用教程(CentOS的远程桌面连接)
    linux awk解析csv文件
    由windows向linux上传下载文件方法
    codeblocks linux编译告警乱码解决办法
  • 原文地址:https://www.cnblogs.com/strugglion/p/6399437.html
Copyright © 2020-2023  润新知