[LeetCode] 10. Regular Expression Matching

本文介绍了一种实现正则表达式匹配的方法,支持‘.’和‘*’两个特殊字符,详细解析了递归和动态规划两种解题思路,并提供了代码示例。

Regular Expression Matching

Given an input string (s) and a pattern §, 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).
Note:
s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
Example 1:

Input:
s = “aa”
p = “a”
Output: false
Explanation: “a” does not match the entire string “aa”.

Example 2:

Input:
s = “aa”
p = “a*”
Output: true
Explanation: ‘*’ means zero or more of the precedeng element, ‘a’. Therefore, by repeating ‘a’ once, it becomes “aa”.

Example 3:

Input:
s = “ab”
p = “."
Output: true
Explanation: ".
” means “zero or more (*) of any character (.)”.

Example 4:

Input:
s = “aab”
p = “cab”
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches “aab”.

Example 5:

Input:
s = “mississippi”
p = “misisp*.”
Output: false

解析

字符串匹配,‘.’可以匹配任何字符,‘*’前面必须有字符,可以为前面字符的0个或者多个。

解法1:递归

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

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

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

  • 若p的第二个字符为*,进行下列循环,条件是若s不为空且首字符匹配(包括p[0]为点),调用递归函数匹配s和去掉前两个字符的p(这样做的原因是假设此时的星号的作用是让前面的字符出现0次,验证是否匹配),若匹配返回true,否则s去掉首字母(因为此时首字母匹配了,我们可以去掉s的首字母,而p由于星号的作用,可以有任意个首字母,所以不需要去掉),继续进行循环。

  • 返回调用递归函数匹配s和去掉前两个字符的p的结果(这么做的原因是处理星号无法匹配的内容,比如s=“ab”, p=“ab",直接进入while循环后,我们发现"ab"和"b"不匹配,所以s变成"b",那么此时跳出循环后,就到最后的return来比较"b"和"b"了,返回true。再举个例子,比如s="", p="a”,由于s为空,不会进入任何的if和while,只能到最后的return来比较了,返回true,正确)。

简洁来说就是
当p为空,返回s是否为空;
当p大于1时并且第二个为’*'时,分为两种,一种是星号表示为前面0个,一种是s不为空,并且s和p的第一个匹配时,p往后移两位
否则,一个一个匹配。

class Solution {
public:
    bool isMatch(string s, string p) {
        int len1 = s.size();
        int len2 = p.size();
        if(len2==0)
            return len1==0;
        if(len2>1 && p[1]=='*')
            return isMatch(s,p.substr(2)) ||
                (len1 && (s[0]==p[0] || p[0]=='.') && isMatch(s.substr(1),p));
        else
            return (len1 && (s[0]==p[0] || p[0]=='.') && isMatch(s.substr(1),p.substr(1)));
    }
};

解法2:动态规划

用一个二维数组dp[i][j]表示s的前i个字符和p的前j个字符匹配。

  1. dp[i][j] = dp[i-1][j-1]: s[i]==p[j] || p[j]=’.’
  2. 当p[j]==’*"时,分为两种情况:
    s[i] != p[j-1],此星号表示为空,dp[i][j] = dp[i][j-2]
    s[i] = p[j-1],dp[i][j] = dp[i][j-2] 星号表示重复0次
    dp[i][j] = dp[i][j-1] 星号表示重复1次
    dp[i][j] = dp[i-1][j] 星号表示重复多次

初始化dp,

dp[0][0]=true;
第0列,初始化为false,
for (int i = 1; i <= m; i++)
dp[i][0] = false;
第0行,只有X能匹配空串,如果有,它的真值一定和dp[0][j-2]的相同(略过它之前的符号)
for (int j = 1; j <= n; j++)
dp[0][j] = j > 1 && ‘*’ == p[j - 1] && dp[0][j - 2];

class Solution {
public:
    bool isMatch(string s, string p) {
        int len1 = s.size();
        int len2 = p.size();
        vector<vector<bool> > dp(len1+1, vector<bool>(len2+1,false));
        dp[0][0]=1;
        for(int i=1;i<=len1;i++)
            dp[i][0] = false;
        for(int j=1;j<=len2;j++)
            dp[0][j] = j>1 && '*' == p[j-1] && dp[0][j-2];
        for(int i=1;i<=len1;i++){
            for(int j=1; j<=len2;j++){
                if(p[j-1]=='*')
                    dp[i][j] = dp[i][j-2] || (s[i-1] == p[j-2] || p[j-2] == '.') && dp[i-1][j];
                else
                    dp[i][j] = (p[j-1] == '.' || s[i-1] == p[j-1]) && dp[i-1][j-1];
            }
        }
        return dp[len1][len2];        
    }
};

参考

https://hk029.gitbooks.io/leetbook/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/010.%20Regular%20Expression%20Matching/010.%20Regular%20Expression%20Matching.html
http://www.cnblogs.com/grandyang/p/4461713.html

LeetCode Problem 10, known as Regular Expression Matching, involves determining if a string matches a given pattern, considering special characters like `.` and `*`. The solution typically requires a recursive approach or dynamic programming to handle the complexities introduced by these special characters. Below is a Java implementation using recursion to solve this problem: ```java public class Solution { public boolean isMatch(String s, String p) { if (s == null || p == null) { return false; } int lenS = s.length(); int lenP = p.length(); if (lenP == 0) { return lenS == 0; } if (lenP == 1) { if (p.equals(s) || p.equals(".")) { return true; } else { return false; } } if (p.charAt(1) == '*') { while (s.length() > 0 && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.')) { if (isMatch(s, p.substring(2))) { return true; } s = s.substring(1); } return isMatch(s, p.substring(2)); } else { if (s.length() > 0 && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.')) { return isMatch(s.substring(1), p.substring(1)); } return false; } } } ``` This solution recursively checks for matches by evaluating the current characters and handling the `*` wildcard, which allows for zero or more of the preceding element. The function proceeds by either skipping the current pattern and the next (if the next character is `*`) or by matching the current character and proceeding in both strings. The implementation efficiently breaks down the problem into manageable recursive calls, ensuring all possible matches are explored, especially when dealing with the `*` operator, which introduces backtracking possibilities to find a valid match path[^2].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值