LeetCode刷題實戰10:字符串正則匹配

算法的重要性,我就很少說了吧,想去大廠,就必需要通過基礎知識和業務邏輯面試+算法面試。因此,爲了提升你們的算法能力,這個號後續天天帶你們作一道算法題,題目就從LeetCode上面選 !面試

 

今天和你們聊的問題叫作正則表達式匹配,咱們先來看題面:正則表達式

 

 

Given an input string (s) and a pattern (p), implement regular expression
matching with support for '.' and '*'.算法

'.' Matches any single character.
'*' Matches zero or more of the preceding element.express

The matching should cover the entire input string (not partial).數組

 

https://leetcode.com/problems/regular-expression-matching/ide

 

Note:code

  • 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 *.

 

題意

 

這道題屬於典型的人狠話很少的問題,讓咱們動手實現一個簡單的正則匹配算法。不過爲了下降難度,這裏須要匹配的只有兩個特殊符號,一個符號是'.',表示能夠匹配任意的單個字符。還有一個特殊符號是'*',它表示它前面的符號能夠是任意個,能夠是0個。遞歸

 

題目要求是輸入一個母串和一個模式串,請問是否可以達成匹配。ip

 

 

示例 1:輸入:s = "aa"p = "a"輸出: false解釋: "a" 沒法匹配 "aa" 整個字符串。示例 2:輸入:s = "aa"p = "a*"輸出: true解釋: 由於 '*' 表明能夠匹配零個或多個前面的那一個元素, 在這裏前面的元素就是 'a'。所以,字符串 "aa" 可被視爲 'a' 重複了一次。示例 3:輸入:s = "ab"p = ".*"輸出: true解釋: ".*" 表示可匹配零個或多個('*')任意字符('.')。示例 4:輸入:s = "aab"p = "c*a*b"輸出: true解釋: 由於 '*' 表示零個或多個,這裏 'c' 爲 0 個, 'a' 被重複一次。所以能夠匹配字符串 "aab"。示例 5:輸入:s = "mississippi"p = "mis*is*p*."輸出: false題解element

 

 

這題要求的是徹底匹配,而不是包含匹配。也就是說s串匹配完p串以後不能有剩餘,好比恰好徹底匹配才行。明確了這點以後,咱們先來簡化操做,假設不存在'*'這個特殊字符,只存在'.',那麼顯然,這個簡化事後的問題很是簡單,咱們隨便就能夠寫出代碼:

  •  
def match(s, p):  n = len(s)  for i in range(n):    if s[i] == p[i] or p[i] == '.':      continue    return False  return True

 

咱們下面考慮加入'*'的狀況,其實加入'*'只會有一個問題,就是'*'能夠匹配任意長度,若是當前位置出現了'*',咱們並不知道它應該匹配到哪裏爲止。咱們不知道須要匹配到哪裏爲止,那麼就須要進行搜索了。也就是說,咱們須要將它轉換成一個搜索問題來進行求解。咱們試着用遞歸來寫一下:

  •  
def match(s, p, i, j):    # 當前位置是否匹配    flag = s[i] == p[j] or p[j] == '.'    # 判斷p[j+1]是不是*,若是是那麼說明p[j]能夠跳過匹配    if j+1 < len(p) and p[j+1] == '*':        # 兩種狀況,一種是跳過p[j],另外一種是p[j]繼續匹配        return match(s, p, i, j+2) or (flag and match(s, p, i+1, j))    else:        # 若是沒有*,只有一種可能        return flag and match(s, p, i+1, j+1)

 

這段代碼的精髓在於,因爲'*'以前的符號也能夠是0個,因此咱們不能判斷當前位置是否會是'*',而要判斷後面一個位置是不是'*'。這句話看起來有些像是繞口令,可是倒是這道題的精髓,若是看不懂的話,能夠結合一下代碼思考。總之,就是以出否出現'*'爲基點,分狀況進行遞歸便可。從代碼上來看算上註釋才12行,但是將這裏面的關係都梳理清楚,並不容易。仍是很是考驗基本功的,須要對遞歸有較深刻的理解才行。不過,這並非最好的方法,由於你會發現有不少狀態被重複計算了不少次。這也是遞歸算法常常遇到的問題之一,要解決倒也不難,咱們很容易發現,對於固定的i和j,答案是固定的。那麼,咱們能夠用一個數組來存儲全部的i和j的狀況。若是當前的i和j處理過了,那麼直接返回結果,不然再去計算。這種方法稱做記憶化搜索,提及來複雜,可是實現起來只須要加幾行代碼:

  •  
memory = {}def match(s, p, i, j):    if (i, j) in memory:      return memory[(i, j)]    # 當前位置是否匹配    flag = s[i] == p[j] or p[j] == '.'    # 判斷p[j+1]是不是*,若是是那麼說明p[j]能夠跳過匹配    if j+1 < len(p) and p[j+1] == '*':        # 兩種狀況,一種是跳過p[j],另外一種是p[j]繼續匹配        ret = match(s, p, i, j+2) or (flag and match(s, p, i+1, j))    else:        # 若是沒有*,只有一種可能        ret = flag and match(s, p, i+1, j+1)    memory[(i, j)] = ret    return ret

 

若是你對動態規劃足夠熟悉的話,想必也應該知道,記憶化搜索本質也是動態規劃的一種實現方式。但一樣,咱們也能夠選擇其餘的方式實現動態規劃,就能夠擺脫遞歸了,相比於遞歸,使用數組存儲狀態的遞推形式更容易理解。

 

咱們用dp[i][j]存儲s[:i]與p[:j]是否匹配,那麼根據咱們以前的結論,若是p[j-1]是'*',那麼dp[i][j]可能由dp[i][j-2]或者是dp[i-1][j]轉移獲得。dp[i][j-2]比較容易想到,就是'*'前面的字符做廢,爲何是dp[i-1][j]呢?這種狀況是表明'*'連續匹配,由於可能匹配任意個,因此必需要匹配在'*'這個位置。

 

舉個例子:

s = 'aaaaa'
p = '.*'

 

在上面這個例子裏,'.'能匹配全部字符,可是問題是s中只有一個a能匹配上。若是咱們不用dp[i-1][j]而用dp[i-1][j-1]的話,那麼是沒法匹配aa或者aaa這種狀況的。由於這幾種狀況都是經過'*'的多匹配能力實現的。若是還不理解的同窗, 建議仔細梳理一下它們之間的關係。

 

咱們用數組的形式寫出代碼:

 

  •  
def is_match(s, p):    # 爲了防止超界,咱們從下標1開始    s = '$' + s    p = '$' + p    n, m = len(s), len(p)    dp = [[False for _ in range(m)] for _ in range(n)]
dp[0][0] = True
# 須要考慮s空串匹配的狀況 for i in range(n): for j in range(1, m): # 標記當前位置是否匹配,主要考慮s爲空串的狀況 match = True if i > 0 and (s[i] == p[j] or p[j] == '.') else False # 判斷j位置是否爲'*' if j > 1 and p[j] == '*': # 若是是,只有兩種轉移的狀況,第一種表示略過前一個字符,第二種表示重複匹配 dp[i][j] = dp[i][j-2] or ((s[i] == p[j-1] or p[j-1] == '.') and dp[i-1][j]) else: # 若是不是,只有一種轉移的可能 dp[i][j] = dp[i-1][j-1] and match return dp[n-1][m-1]

這題的代碼並不長,算上註釋也不過20行左右。可是當中對於題意的思考,以及對算法的運用很高,想要AC難度仍是不低的。但願你們可以多多揣摩,理解其中的精髓,尤爲是最後的動態規劃解法,很是經典,推薦必定要弄懂。固然若是實在看不明白也沒有關係,在之後的文章,我會給你們詳細講解動態規劃算法。今天的文章就到這裏。

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

相關文章
相關標籤/搜索