leetcode上的第10题正则表达式匹配

官方题解的代码比较精炼,确实很难理解,为了能使题主理解,说明如下

对字符串s1,s2 s1+s2表示字符串的连接,用null表示空串 对一个字符c1 和一个.或字符c2 用match(c1,c2)表示它们是否匹配

设要验证匹配与否的字符串为S,长度m+1,设0<=i<=j<=m,则由S在索引位置i和索引位置j之间所有索引位置上的字符(包括索引位置i和索引位置j)构成的子串记为S[i,j],S在索引位置k(0<=k<=m)上的字符记为S[k]

设正则表达式字符串为P,长度为n+1,同理可定义Pi,j和Pk

现对1<=i<=m和1<=j<=n,考察S[0,i]和P[0,j]

如果S[0,i]和P[0,j]匹配那么

如果P[j]=*此时注意P[j-1,j]要么匹配假想的接在S[0,i]之后的空串null要么匹配全部由和P[j-1]匹配的字符组成的S[0,i]的后缀Sk,i

如果match(S[i],P[j-1])=false

此时P[j-1,j]只能匹配假想的接在S[0,i]之后的空串null,又S[0,i]和P[0,j]匹配即S[0,i]+null和P[0,j]匹配,故必有S[0,i]匹配P[0,j-2]

如果match(S[i],P[j-1])=true

同理如果P[j-1,j]匹配假想的接在S[0,i]之后的空串null,则S[0,i]匹配P[0,j-2]

否则P[j-1,j]匹配上文所提S[k,i],由于S[0,i]和P[0,j]匹配即S[0,k-1]+S[k,i]和P[0,j]匹配故必有

S[0,k-1]匹配P[0,j-2] (若S[0,k-1]和P[0,j-2]不存在则视为空串null对证明过程没有影响)

若k = i 则令P[j -1,j]匹配null,则P[0,j-2]+P[j-1,j]匹配S[0,k-1]+null即S[0,i-1]

从而S[0,i-1]匹配P[0,j]

若k<i注意到S[k,i-1]全部由和P[j-1]匹配的字符组成,故有S[k,i-1]匹配P[j-1,j],从而S[0,k-1]+S[k,i-1]匹配P[0,j-2]+P[j-1,j],即S[0,i-1]匹配P[0,j]

如果P[j]!=*,则P[j,j]只匹配单个字符,P[j,j]与S[i,i]严格对应,P[j,j]要么匹配S[i,i]要么不匹配S[i,i],但由假设S[0,i]和P[0,j]匹配故必有P[j,j]匹配Si,i这样很容易得到S[0,i-1]匹配P[0,j-1]

综上我们得到命题一

如果S[0,i]和P[0,j]匹配则

如果P[j]=*则

如果match(S[i],P[j-1])=false则S[0,i]匹配P[0,j-2]

如果match(S[i],P[j-1])=true则S[0,i]匹配P[0,j-2]和S[0,i-1]匹配P[0,j]中必有一个成立

如果P[j]!=*则S[0,i-1]匹配P[0,j-1],P[j,j]匹配S[i,i]

命题一的逆命题命题二同样成立

事实上如果P[j]=*

如果match(S[i],P[j-1])=false则由逆命题假设S[0,i]匹配P[0,j-2] 我们令P[j-1,j]匹配null,则S[0,i]+null匹配P[0,j-2]+P[j-1,j]则S[0,i]匹配P[0,j]

如果match(S[i],P[j-1])=true则由逆命题假设S[0,i]匹配P[0,j-2]和S[0,i-1]匹配P[0,j]中必有一个成立

若S[0,i-1]匹配P[0,j]成立

根据命题一证明过程中的相关内容有

若P[j-1,j]匹配接在S[0,i-1]之后的空串null则S[0,i-1]匹配P[0,j-2],但match(S[i],P[j-1])=true所以S[i,i]匹配P[j-1,j]

因此S[0,i-1]+S[i,i]匹配P[0,j-2]+P[j-1,j]即S[0,i]匹配P[0,j]

若P[j-1,j]匹配S[0,i-1]的后缀Sk,i-1,注意S[0,i-1]匹配P[0,j]因此S[0,k-1]匹配P0,j-2

另外S[k,i-1]全部由和P[j-1]匹配的字符组成而match (S[i],P[j-1])=true因此S[k,i]全部由P[j-1]组成故S[k,i]匹配P[j-1,j]从而

S[0,k-1]+S[k,i]匹配P[0,j-2]+P[j-1,j]故S[0,i]匹配P[0,j]

如果S[0,i]匹配P[0,j-2]成立照搬match(S[i],P[j-1])=false情形的证明同样有S[0,i]匹配P[0,j]

如果P[j]!=*由逆命题假设有S[0,i-1]匹配P[0,j-1],P[j,j]匹配S[i,i]此时显然有S[0,i]匹配P[0,j]

这样就证明了命题二的正确性

综合命题一命题二我们得到命题三

如果P[j]=*则

如果match(S[i],P[j-1])=false 若S[0,i]匹配P[0,j-2]则S[0,i]和P[0,j]匹配否则不匹配

如果match(S[i],P[j-1])=true若S[0,i]匹配P[0,j-2]和S[0,i-1]匹配P[0,j]中必有一个成立则S[0,i]和P[0,j]匹配否则不匹配

如果P[j]!=* 若S[0,i-1]匹配P[0,j-1],P[j,j]匹配S[i,i]则S[0,i]和P[0,j]匹配否则不匹配

如果我们用dp[i][j]表示S[0,i]和P[0,j]是否匹配,为true表示匹配为false表示不匹配则由命题三可得

对1<=i<=m

1<=j<=n

如果P[j]=*则

如果match(S[i],P[j-1])=false dp[i][j] = j > 1 && dp[i][j-2]

如果match(S[i],P[j-1])=true dp[i][j] = (j > 1 && dp[i][j-2]) || dp[i-1][j]

如果P[j]!=* dp[i][j] = dp[i-1][j-1] && match(S[i],P[j])

现在再回过头看官方题解代码,题主应该明白了吧

另外再提一下dp[i][0]1<=i <=m

的计算是很容易的事情 题主可以自己思考

dp[0][j] 0<=j <=n的计算我们可以照葫芦画瓢,仿照命题一命题二的证明方法得到命题四

对1<=j<=n有

P[0,j]匹配S[0,0]当且仅当

如果P[j]=*

如果match(S[0],P[j-1])=true

S[0,0]匹配P[0,j-2] j>1 或null匹配P0,j-2

如果match(S[0],P[j-1])=false

S[0,0]匹配P[0,j-2] j>1

如果P[j]!=*

null匹配P[0,j-1]且match(S[0], P[j]) = true

类似的,我们可以把命题四转换成伪代码

item_larger_than_one = false

pre_has_item = false

pre_pre_has_item = false

for (j from 0 to n)

if (P[j] == *)

if (match(S[0],P[j-1]) == true)

d[0][j]=(j>1 && d[0][j-2]) || pre_pre_has_item == false

else

d[0][j] = j>1 && d[0][j-2]

pre_pre_has_item = pre_has_item

if (item_larger_than_one == false)

pre_has_item = false

else

d[0][j] = pre_has_item == false && match(S[0], P[j]) == true

if (pre_has_item)

item_larger_than_one = true

pre_pre_has_item = pre_has_item

pre_has_item = true

这里pre_has_item指P在当前索引j前的部分是否存在项,项指由.或一个不为的字符组成的不和关联的独立的子匹配表达式,pre_pre_has_item指P在索引j-1前的部分是否存在项 item_larger_than_one指P在索引j前的部分的项的数目是否大于一,事实上,p[0,j]能够匹配空串当且仅当p[0,j]中不含任何项,有了上述说明,pre_has_item,pre_pre_has_item和item_larger_than_one的更新策略题主可以自己体会

另外,尽管命题四j>=1但以上伪代码在j==0时仍然可以正确运行,题主可以自己模拟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值