Leetcode高級(jí)挑戰(zhàn):正則表達(dá)式匹配

《正則表達(dá)式匹配》在Leetcode屬于難度為Hard的問(wèn)題褒傅,我自己花了半天,使用遞歸的方式完成了題目粪躬,但是程序效率很低担败。后來(lái)看了“標(biāo)準(zhǔn)答案”,花了一些時(shí)間研究才完全理解镰官,這篇文章我嘗試解釋“標(biāo)準(zhǔn)答案”使用的算法提前。

問(wèn)題

給出一個(gè)字符串(s)和一個(gè)表達(dá)式(p),實(shí)現(xiàn)一個(gè)支持'.'和''正則表達(dá)式的匹配泳唠。

'.' 匹配任意單個(gè)字符
'*' 匹配0個(gè)或多個(gè)前一個(gè)字符

表達(dá)式必須匹配整個(gè)字符串而不是其中一部分

Note:
  • s 可以為空或非空字符串狈网,字符串中字符的取值范圍為 a-z
  • p 可以為空或非空字符串,字符串中字符的取值范圍為 a-z, 還可以包含兩個(gè)特殊字符"."和"*”
例1:
Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
例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".

例3:
Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".
例4:
Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches "aab".

例5:
Input:
s = "mississippi"
p = "mis*is*p*."
Output: false

“標(biāo)準(zhǔn)答案”算法

  1. 構(gòu)造一個(gè)二維數(shù)組dp
    boolean[][] dp = new boolean[s.length()+1][p.length()+1];
  1. 初始化dp
    dp[0][0] = true;
    for (int i = 0; i < p.length(); i++) {
        if (p.charAt(i) == '*' && dp[0][i-1]) {
            dp[0][i+1] = true;
        }
    }
  1. 遍歷字符串s和字符串p中的字符
    for (int i = 0 ; i < s.length(); i++) {
        for (int j = 0; j < p.length(); j++) {
            if (p.charAt(j) == '.') {
                // 該字符匹配成功笨腥,使用去掉1個(gè)字符的s子串和p子串的匹配結(jié)果拓哺,作為當(dāng)前子串的匹配結(jié)果
                dp[i+1][j+1] = dp[i][j];
            }
            if (p.charAt(j) == s.charAt(i)) {
                // 該字符匹配成功株旷,使用去掉1個(gè)字符的s子串和p子串的匹配結(jié)果沼侣,作為當(dāng)前子串的匹配結(jié)果
                dp[i+1][j+1] = dp[i][j];
            }
            if (p.charAt(j) == '*') {
                // 如果遇到 '*'
                if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') {
                    // 該字符和p中上一個(gè)字符匹配失敗,使用當(dāng)前s子串和去掉2個(gè)字符的p子串的匹配結(jié)果豪治,作為當(dāng)前子串的匹配結(jié)果
                    dp[i+1][j+1] = dp[i+1][j-1];
                } else {
                    // 該字符和p中上一個(gè)字符匹配成功谆级,使用下面三個(gè)匹配結(jié)果作與運(yùn)算后的結(jié)果烤礁,作為當(dāng)前子串的匹配結(jié)果
                    // 1. s子串去掉一個(gè)字符,和當(dāng)前p子串的匹配結(jié)果 (in this case, a* counts as multiple a )
                    // 2. s子串和p子串去掉1個(gè)字符的匹配結(jié)果(in this case, a* counts as single a)
                    // 3. s子串和p子串去掉2個(gè)字符的匹配結(jié)果(in this case, a* counts as empty)
                    dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]);
                }
            }
        }
    }
    
  1. 返回最終結(jié)果
    return dp[s.length()][p.length()];

概述

該算法的核心是肥照,通過(guò)二維遍歷脚仔,依次計(jì)算所有s子串和p子串的匹配結(jié)果,最后的匹配結(jié)果舆绎,就是整個(gè)s串和p串的匹配結(jié)果

假設(shè) s="xaabyc" p="xa*b.c"

1玻侥,初始化

    dp[0][0] = true;
    for (int i = 0; i < p.length(); i++) {
        if (p.charAt(i) == '*' && dp[0][i-1]) {
            dp[0][i+1] = true;
        }
    }

  • 空串和空串匹配,結(jié)果為T(mén)rue亿蒸,所以 dp[0][0] = true
s\p 0 x a * b . c
0 T F F F F F F
x F
a F
a F
b F
y F
c F

2凑兰,遍歷字符串s和字符串p中的字符

for (int i = 0 ; i < s.length(); i++) {
        for (int j = 0; j < p.length(); j++) {
            if (p.charAt(j) == '.') {
                // 該字符匹配成功,使用去掉1個(gè)字符的s子串和p子串的匹配結(jié)果边锁,作為當(dāng)前子串的匹配結(jié)果
                dp[i+1][j+1] = dp[i][j];
            }
            if (p.charAt(j) == s.charAt(i)) {
                // 該字符匹配成功姑食,使用去掉1個(gè)字符的s子串和p子串的匹配結(jié)果,作為當(dāng)前子串的匹配結(jié)果
                dp[i+1][j+1] = dp[i][j];
            }
            if (p.charAt(j) == '*') {
                // 如果遇到 '*'
                if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') {
                    // 該字符和p中上一個(gè)字符匹配失敗茅坛,使用當(dāng)前s子串和去掉2個(gè)字符的p子串的匹配結(jié)果音半,作為當(dāng)前子串的匹配結(jié)果
                    dp[i+1][j+1] = dp[i+1][j-1];
                } else {
                    // 該字符和p中上一個(gè)字符匹配成功则拷,使用下面三個(gè)匹配結(jié)果作與運(yùn)算后的結(jié)果,作為當(dāng)前子串的匹配結(jié)果
                    // 1. s子串去掉一個(gè)字符曹鸠,和當(dāng)前p子串的匹配結(jié)果 (in this case, a* counts as multiple a )
                    // 2. s子串和p子串去掉1個(gè)字符的匹配結(jié)果(in this case, a* counts as single a)
                    // 3. s子串和p子串去掉2個(gè)字符的匹配結(jié)果(in this case, a* counts as empty)
                    dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]);
                }
            }
        }
    }

Loop1:i=1, j=1 to 6

  • s[1] = 'x', j[1] = 'x'
  • s[1] == j[1], 所以 dp[1][1] = dp[0][0]煌茬,結(jié)果為T(mén)rue
  • s[1] != j[2,3,4,5,6] 所以 dp[1][2,3,4,5,6] = False
s\p 0 x a * b . c
0 T F F F F F F
x F T F F F F F
a F
a F
b F
y F
c F

Loop2:i=2, j=1 to 6

  • 當(dāng) i = 2, j = 3 是,s[i] = 'a', p[j] = '*'
  • 這個(gè)時(shí)候s的子串 “xa” 既匹配 "xa"彻桃,也匹配 "xa*"
  • 所以 dp[2][2] = True, dp[2][3] = True
s\p 0 x a * b . c
0 T F F F F F F
x F T F F F F F
a F F T T (注意) F F F
a F
b F
y F
c F

后面的邏輯依次類(lèi)推坛善,最后得出最終結(jié)果如下

s\p 0 x a * b . c
0 T F F F F F F
x F T F F F F F
a F F T T F F F
a F F F T F F F
b F F F F T F F
y F F F F F T F
c F F F F F F T (最終結(jié)果)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市邻眷,隨后出現(xiàn)的幾起案子眠屎,更是在濱河造成了極大的恐慌,老刑警劉巖肆饶,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件改衩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驯镊,警方通過(guò)查閱死者的電腦和手機(jī)葫督,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)板惑,“玉大人橄镜,你說(shuō)我怎么就攤上這事∪鞣牛” “怎么了蛉鹿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)往湿。 經(jīng)常有香客問(wèn)我妖异,道長(zhǎng),這世上最難降的妖魔是什么领追? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任他膳,我火速辦了婚禮,結(jié)果婚禮上绒窑,老公的妹妹穿的比我還像新娘棕孙。我一直安慰自己,他們只是感情好些膨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布蟀俊。 她就那樣靜靜地躺著,像睡著了一般订雾。 火紅的嫁衣襯著肌膚如雪肢预。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天洼哎,我揣著相機(jī)與錄音烫映,去河邊找鬼沼本。 笑死,一個(gè)胖子當(dāng)著我的面吹牛锭沟,可吹牛的內(nèi)容都是我干的抽兆。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼族淮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辫红!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瞧筛,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤厉熟,失蹤者是張志新(化名)和其女友劉穎导盅,沒(méi)想到半個(gè)月后较幌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡白翻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年乍炉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤馍。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岛琼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巢株,到底是詐尸還是另有隱情槐瑞,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布阁苞,位于F島的核電站困檩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏那槽。R本人自食惡果不足惜悼沿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骚灸。 院中可真熱鬧糟趾,春花似錦、人聲如沸甚牲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丈钙。三九已至非驮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間著恩,已是汗流浹背院尔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工蜻展, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邀摆。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓纵顾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親栋盹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子施逾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容