Leetcode 10 正则表达式匹配

本文介绍如何使用C++实现字符串'sss'与字符规律'ppp'的正则表达式匹配,包括'.'匹配任意字符和'*'匹配零个或多个的情况,通过递归和状态转移矩阵解决复杂匹配问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你一个字符串 s s s 和一个字符规律 p p p,请你来实现一个支持 '.''*' 的正则表达式匹配。

'.' 匹配任意单个字符;
'*' 匹配零个或多个前面的那一个元素;
所谓匹配,是要涵盖 整个字符串 s s s 的,而不是部分字符串。

示例 1

输入: s = s = s="aa" p = p = p= "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例 2

输入: s = s = s= "aa" p = p = p= "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa"可被视为 'a' 重复了一次。

示例 3

输入: s = s = s= "ab" $p = $".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4

输入: s = s = s= "aab" $p = $"c*a*b"
输出:true
解释:因为 '*' 表示零个或多个,这里 'c' 0 0 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"

示例 5

输入: s = s = s= "mississippi" p = p = p= "mis*is*p*."
输出:false

提示

0 ≤ ∣ s ∣ ≤ 20 0\le |s|\le 20 0s20
0 ≤ ∣ p ∣ ≤ 30 0\le |p|\le 30 0p30
s s s 可能为空,且只包含小写字母。
p p p 可能为空,且只包含小写字母,以及字符 '.''*'
保证每次出现字符 * 时,前面都匹配到有效的字符。

思路

默认字符串下标都从 1 1 1 开始,从 0 0 0 开始的都是sb。定义 f ( i , j ) f(i,j) f(i,j) s 1 s 2 ⋯ s i s_{1}s_2\cdots s_i s1s2si p 1 p 2 ⋯ p j p_1p_2\cdots p_j p1p2pj 匹配的真值。

下标从 1 1 1 开始便于考虑边界条件,两个空字符串匹配, f ( 0 , 0 ) = t r u e f(0,0)=true f(0,0)=true;空字符串与形如a*b*c*...的字符串也可匹配,即 f ( 0 , j ) = j ≥ 2 ∧ p j = f(0,j)=j\ge 2\wedge p_j= f(0,j)=j2pj= * ∧ f ( 0 , j − 2 ) \wedge f(0,j-2) f(0,j2)

最优子结构: s s s p p p 匹配,则 s s s p p p 必能分别分成 k k k 段子串,使得 k k k 段子串分别匹配。

无后效性:若 s i s_i si p j p_j pj 的匹配方式确定,则此后的匹配只与当前的状态有关。

s i s_i si 必为小写字母,我们需要考虑 p j p_j pj 的字符类型。

  • p j p_j pj 为小写字母,必须要让 s i = p j s_i=p_j si=pj s 1 s 2 ⋯ s i − 1 s_1s_2\cdots s_{i-1} s1s2si1 p 1 p 2 ⋯ p j − 1 p_1p_2\cdots p_{j-1} p1p2pj1 匹配,才能使得 s 1 s 2 ⋯ s i s_1s_2\cdots s_{i} s1s2si p 1 p 2 ⋯ p j p_1p_2\cdots p_j p1p2pj 匹配, f ( i , j ) = f ( i − 1 , j − 1 ) ∧ ( s i = p j ) f(i,j)=f(i-1,j-1)\wedge (s_i=p_j) f(i,j)=f(i1,j1)(si=pj)

  • p j p_j pj.,可以匹配任意字符,只需要 s 1 s 2 ⋯ s i − 1 s_1s_2\cdots s_{i-1} s1s2si1 p 1 p 2 ⋯ p j − 1 p_1p_2\cdots p_{j-1} p1p2pj1 匹配,就能使得 s 1 s 2 ⋯ s i s_1s_2\cdots s_i s1s2si p 0 p 1 ⋯ p j p_0p_1\cdots p_j p0p1pj 匹配, f ( i , j ) = f ( i − 1 , j − 1 ) f(i,j)=f(i-1,j-1) f(i,j)=f(i1,j1)

  • p j p_j pj*,有如下两种选择:

    • 扩展 p j − 1 p_{j-1} pj1,即形成 p j − 1 p j − 1 ⋯ p j − 1 p_{j-1}p_{j-1}\cdots p_{j-1} pj1pj1pj1 p j − 1 p_{j-1} pj1 的个数不少于 1 1 1
    • 不扩展 p j − 1 p_{j-1} pj1,即 p j − 1 p_{j-1} pj1 不出现;
    • s i ≠ p j − 1 s_i\neq p_{j-1} si=pj1 p j − 1 ≠ p_{j-1}\neq pj1= .,只有 p j − 1 p_{j-1} pj1 不出现才有可能使得 s 1 s 2 ⋯ s i s_1s_2\cdots s_i s1s2si p 1 p 2 ⋯ p j p_1p_2\cdots p_j p1p2pj 匹配上,即 f ( i , j ) = f ( i , j − 2 ) f(i,j)=f(i,j-2) f(i,j)=f(i,j2)
    • s i = p j − 1 s_i=p_{j-1} si=pj1 p j − 1 = p_{j-1}= pj1= .,可以兼顾上述两种选择;当选择不扩展 p j − 1 p_{j-1} pj1 f ( i , j ) = f ( i , j − 1 ) f(i,j)=f(i,j-1) f(i,j)=f(i,j1);当选择扩展 p j − 1 p_{j-1} pj1 f ( i , j ) = f ( i − 1 , j − 2 ) f(i,j)=f(i-1,j-2) f(i,j)=f(i1,j2);实际上,选择扩展时,匹配的是形如 a*aaaaaaa... 这样的字符串,若 s i = s i − 1 = ⋯ s i − k s_i=s_{i-1}=\cdots s_{i-k} si=si1=sik f ( i , j ) = ⋁ x = 1 k + 1 f ( i − x , j − 2 ) f(i,j)=\bigvee\limits_{x=1}^{k+1}f(i-x,j-2) f(i,j)=x=1k+1f(ix,j2),本质上是匹配了 s i s_i si,然后将 s i s_i si 扔掉,用 p j − 1 p j p_{j-1}p_j pj1pj 继续匹配 s i − 1 s_{i-1} si1,故 f ( i , j ) = f ( i − 1 , j ) f(i,j)=f(i-1,j) f(i,j)=f(i1,j)

    代码

    class Solution {
    	public:
    	bool isMatch(string s, string p) {
    		const int m = s.length(), n = p.length();
    		s = '\0' + s;
    		p = '\0' + p;
    		bool** f = new bool* [m + 1];
    		for (int i = 0; i <= m; i++) {
    			f[i] = new bool[n + 1];
    			memset(f[i], false, sizeof(bool) * (n + 1));
    		}
    		f[0][0] = true;
    		for (int j = 1; j <= n; j++) {
    			if (j >= 2 && p[j] == '*') {
    				f[0][j] = f[0][j - 2];
    			}
    		}
    		for (int i = 1; i <= m; i++) {
    			for (int j = 1; j <= n; j++) {
    				if (isalpha(p[j])) {
    					f[i][j] = (s[i] == p[j]) & f[i - 1][j - 1];
    				} else if (p[j] == '.') {
    					f[i][j] = f[i - 1][j - 1];
    				} else {
    					if (s[i] == p[j - 1] || p[j - 1] == '.') {
    						f[i][j] = f[i - 1][j];
    						if (j >= 2) {
    							f[i][j] |= f[i][j - 2];
    						}
    					} else if (j >= 2) {
    						f[i][j] = f[i][j - 2];
    					}
    				}
    			}
    		}
    		bool ans = f[m][n];
    		for (int i = 0; i <= m; i++) {
    			delete[]f[i];
    		}
    		return ans;
    	}
    };
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值