LeetCode精讲题 10正则表达式匹配(动态规划)
標(biāo)題
- 題目描述
- 遞歸(超時)
- 動態(tài)規(guī)劃
- 結(jié)語
題目描述
先點贊再觀看、帥哥靚女養(yǎng)成好習(xí)慣。
10 正則表達式匹配
給你一個字符串 s 和一個字符規(guī)律 p,請你來實現(xiàn)一個支持 ‘.’ 和'*'的正則表達式匹配。
'.'匹配任意單個字符
'*'匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字符串 s的,而不是部分字符串。
說明:
s 可能為空,且只包含從 a-z 的小寫字母。
p 可能為空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。
示例 1:
輸入:
s = “aa”
p = "a"
輸出: false
解釋: “a” 無法匹配 “aa” 整個字符串。
示例 2:
輸入:
s = “aa”
p = "a*"
輸出: true
解釋: 因為 ‘*’ 代表可以匹配零個或多個前面的那一個元素, 在這里前面的元素就是 ‘a(chǎn)’。因此,字符串 “aa” 可被視為 ‘a(chǎn)’ 重復(fù)了一次。
示例 3:
輸入:
s = "ab"
p = ".*"
輸出: true
解釋: ".*" 表示可匹配零個或多個('*')任意字符('.')。
示例 4:
輸入:
s = “aab”
p = “cab”
輸出: true
解釋: 因為 ‘*’ 表示零個或多個,這里 ‘c’ 為 0 個, ‘a(chǎn)’ 被重復(fù)一次。因此可以匹配字符串 “aab”。
示例 5:
輸入:
s = “mississippi”
p = "mis*is*p*."
輸出: false
遞歸(超時)
這題剛開始見到,還以為遇到原題了,因為跟劍指offer的其中一題非常像,劍指offer第52題正則表達式,只不過那題給的兩個char類型的數(shù)組,當(dāng)時弱弱的用遞歸暴力過了。
然后一頓操作把上次遞歸的方法重寫過來,結(jié)果超時了……
但是還是把這種遞歸的思路講一下,遞歸主要進行匹配所有情況,主要是看當(dāng)前位置兩個串串能不能匹配。
需要考慮*情況可以匹配,因為*可以出現(xiàn)0次,1次多次。那么在遇到使用*的如果匹配了,可以通過遞歸實現(xiàn)下面三者方式
- 它可以使用0次(相當(dāng)于跟字符串下一部分匹配 a*aa和aa這個第一個a*可以看成0次)
- 也可以使用1次(在當(dāng)前往后移例如a*aa和aaa轉(zhuǎn)成aa和aa的匹配)
- 也可以使用多次(例如a*和aaa轉(zhuǎn)成a*和aa的匹配)
同樣,如果遇到*不可以匹配,那么就使用0次就行了(b*aa和aa匹配轉(zhuǎn)換成aa和aa匹配)
如果下一個不是*,那就考慮是否相當(dāng)或者模式字符是否為.進行繼續(xù)匹配或者終止就可以,在考慮一些開始結(jié)束情況就可以了,一個大概的思維導(dǎo)圖可以看一下。
這部分實現(xiàn)的代碼如下:
public static boolean isMatch2(String s, String p) {//System.out.println(s+" "+p);if (p.length() == 0)// 模式串為false{if (s.length() == 0)return true;return false;} else if (s.length() == 0) {// 匹配串為0if (p.length() % 2 == 1)return false;else {for (int i = 1; i < p.length(); i += 2) {if (p.charAt(i) != '*')return false;}return true;}} else if (p.length() == 1) {//匹配串長度為1if((s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')&&s.length()==1)//可以匹配return true;else {return false;}} else {// 兩個串串正常長度if(p.charAt(1)=='*')//下一個為*{if(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.')//可以匹配 分別用0次 用若1次 用若干次{return isMatch(s.substring(1), p)||isMatch(s.substring(1), p.substring(2))||isMatch(s, p.substring(2));}else {//不匹配只能用0次return isMatch(s, p.substring(2));} }else {if(s.charAt(0)==p.charAt(0)||p.charAt(0)=='.')return isMatch(s.substring(1), p.substring(1));else {//完全失敗return false;}}}很遺憾的超時了,不過在劍指offer是可以過的,主要遇到這種字符就會很麻煩:
isMatch("aaaaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*c")因為這里面匹配中的a*任意一個都可以使用若干次導(dǎo)致遞歸種類太多爆棧。嚶嚶嚶。
動態(tài)規(guī)劃
這題正確而大眾的解法當(dāng)然是動態(tài)規(guī)劃了,我們知道動態(tài)規(guī)劃重在動態(tài)的規(guī)劃方程。并且當(dāng)前結(jié)果是基于父結(jié)果的。這題剛好就可以使用動態(tài)規(guī)劃來解答。
我們使用我們聲明一個dp[][]=new boolean[匹配串長度+1][模式串長度+1] 的二位數(shù)組用來儲存結(jié)果, 其中dp[i][j]表示匹配串前i個和模式串前j個是否匹配。最終匹配串和模式串是否匹配就是返回dp[匹配串長度][模式串長度].
對于動態(tài)規(guī)劃的問題,我們一般會空余出0號位放在越界等特殊情況,所以我們聲明的二維數(shù)組大小長寬都大1,因為0號在dp[][]表示的是空串的結(jié)果而不是一號位置串的結(jié)果。然后我們在搞動態(tài)規(guī)劃題一般需要以下幾步:
- 聲明dp數(shù)組,理解其含義
- 聲明一些初始情況(一般為0)
- 找正常情況動態(tài)方程式
這里的初始我們是dp[0][0]=true表示兩個空串可以匹配。
我們分析這個dp[i][j] 匹配串前i個,模式串前j個是否匹配.其實這個分析和之前遞歸還是有點相似的:
首先如果模式串pattern第j個如果是*,以下兩種情況任意一種匹配成功即可。
- 如果dp[i][j-2]==true那么dp[i][j]肯定為true,因為可以把它看成一個空串。
- 如果dp[i][j-2]不為true也不要緊,如果匹配串和模式串前一個字符可以匹配并且dp[i-1][j]為true,那么也可以匹配(a*和a )
如果模式串第j個不為*那么就是常規(guī)匹配了,如果當(dāng)前位置字符不匹配,那么就為false,如果當(dāng)前位置匹配且dp[i-1][j-1]==true那么dp[i][j]就為true:
當(dāng)然,以上所有考慮i-1的情況i不能等于0.
綜上分析得到dp方程為:
if(模式串當(dāng)前為*) dp[i][j]==dp[i][j-2]||(dp[i-1][j]&&兩串當(dāng)前字符可以匹配) else dp[i][j]=dp[i-1][j-1]&&兩串當(dāng)前字符可以匹配具體實現(xiàn)需要注意下標(biāo)編號在字符串位置和dp下標(biāo)的含義,具體實現(xiàn)的代碼為:
public static boolean isMatch(String s, String p) {boolean dp[][]=new boolean[s.length()+1][p.length()+1];//默認(rèn)為falsedp[0][0]=true;for(int i=0;i<=s.length();i++){for(int j=1;j<=p.length();j++){if(p.charAt(j-1)=='*')//該位置為*{dp[i][j]=dp[i][j-2];//模式用了0次的看看是否能夠匹配,能匹配最好,不能匹配繼續(xù)if(!dp[i][j])//不能匹配{if(i==0) {continue;}else if(s.charAt(i-1)==p.charAt(j-2)||p.charAt(j-2)=='.')//可以匹配{dp[i][j]=dp[i-1][j];}}}else {//正常字符if(i==0){continue;}else if(s.charAt(i-1)==p.charAt(j-1)||p.charAt(j-1)=='.') {//這個位置可以匹配dp[i][j]=dp[i-1][j-1];}} }}return dp[s.length()][p.length()]; }結(jié)語
今天又get一個動態(tài)規(guī)劃題,以前沒有用動態(tài)規(guī)劃的思維去想過,但是這題還是挺好的,至于一些其他的方法如果后面有時間可以繼續(xù)拓展。
原創(chuàng)不易,最后我請你幫兩件事幫忙一下:
star支持一下, 您的肯定是我在平臺創(chuàng)作的源源動力。
微信搜索「bigsai」,關(guān)注我的公眾號,不僅免費送你電子書,我還會第一時間在公眾號分享知識技術(shù)。加我還可拉你進力扣打卡群一起打卡LeetCode。
記得關(guān)注、咱們下次再見!
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的LeetCode精讲题 10正则表达式匹配(动态规划)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode 08字符串转整数09回
- 下一篇: LeetCode 11盛水最多的容器12