字符串算法—正則表達式


1. 前文回顧

  在字符串算法—字符串搜索中,我們實現了從一堆字符中搜索某個字符串的高效算法。

  但如果要在一堆字符中找具有某些規律的字符串(要找的字符串是不確定的,但有規律),該如何設計算法?

  本文將介紹NFA算法來解決此問題。

 

2. 正則表達式

  首先,有規律的字符串是長什么樣的呢?例如:ABBBBBBBBBA。

  我們需要用一種方式來表達這個規律。於是我們就有了正則表達式。

  ABBBBBBBBBA可以表達為AB*A。其中B*表示有N個B,N可以為0。

  然后以下均是正則表達式:

  

  

  [A-E]+表示:(A|B|C|D|E)(A|B|C|D|E)*

  

  有了這些表達式后,我們就能讓程序讀懂字符串的規律,然后就是讓它找出含有這規律的字符串在哪了。

  

3. NFA算法

  本算法需要有向圖基礎,如果不了解有向圖的,可以先看一下圖表算法—有向圖

  從例子中開始了解此算法:

  現有正則表達式: ( ( A * B | A C ) D )。

  我們通過某種構建方法(此方法稍后介紹),把這個正則表達式變成一個有向圖:

  

 

  我們將逐個逐個符號來讀這個表達式。圖中的0,1,2,3,...,10,11是階段,當我們抵達第11階段,則說明找到符合這表達式的字符串。

  圖中的線有兩種顏色,紅色的是過渡線,可以不讀階段里面的內容,直接跳到下一個指定階段;藍色是正常線,要先讀階段里面的內容,再改變階段。

  例如:第4階段只能去第5階段;第1階段可以去第2階段、第3階段、第4階段、第6階段。

  現在我們來看一下AAAABD是否符合這個表達式:

  第一個字符為A,我們從第0階段開始:

  

  0,1階段都是括弧,故符合。現在1可以去2、3、4和6,且2和6都是A,與第一個字符匹配,故兩個階段都去:

  

  第二個字符為A,現在可以去2,4,7(其中去2的路線為2-3-2,去4的路線為2-3-4),但匹配第二個字符的只有第2階段,故去第2階段:

  

  第三個字符為A,現在可以去2,4(其中去2的路線為2-3-2,去4的路線為2-3-4),但匹配第三個字符的只有第2階段,故去第2階段:

  

  第四個字符為A,現在可以去2,4(其中去2的路線為2-3-2,去4的路線為2-3-4),但匹配第四個字符的只有第2階段,故去第2階段:

  

  第五個字符為B,現在可以去2,4(其中去2的路線為2-3-2,去4的路線為2-3-4),但匹配第五個字符的只有第4階段,故去第4階段:

  

  第六個字符為D,現在可以去9(其中去9的路線為4-5-8-9,由於5,8都是符號,不能與字符比較,故無視它們),第9階段匹配第五個字符,故去第9階段:

 

  沒有第七個字符,且第9階段可以直接抵達第11階段,故這個字符串符合此表達式。

  如果要從這個字符串中,找出有多少個符合此表達式的字符串,則經過上面的尋找,我們找到了第一個:AAAABD。

  接下來尋找第二個,從第二個字符開始第0階段,如果能順利抵達第11階段,說明AAABD也符合;否則,AAABD不符合。

  然后第三個,從第三個字符開始第0階段,如此類推,直到所有字符都開始過第0階段為止,然后統計有多少個字符串符合,且記住它們的位置。

  如此類推,我們可以找出整段字符中符合這個表達式的所有字符串,從而解決一開始提出的問題。

  接下來就要看如何把這個正則表達式轉化為有向圖了:

  首先,如果某階段里含有的不是符號,而是字符,則可以直接去下一個階段:

  

  然后,如果遇到的是符號 * ,則分兩種情況:

  1. *所處的階段的前一階段是字符:

  

  則加3條方向(不用擔心重復加方向,因為代碼中如果出現重復的,直接覆蓋)。

  2. *所處的階段的前一階段是右括弧:

  

  也是加3條方向:與前一個左括弧加一個互相的方向,我們會用一個數字lp來記住前一個左括弧。

  回到我們的這個例子,給*階段添加方向:(符號用的線都是過渡線

  

  如果遇到的是左括弧或者是 | 則要把它們按順序加到棧中,順便給左括弧加一個指向下一個階段的方向:(新建棧A)

  

 

  如果遇到的是右括弧,則給它加一個指向下一個階段的方向,且棧A輸出數個元素,直到見到左括弧為止:

  

 

 

  然后棧A輸出一個元素(先進先出):第5階段的 | ,新建一個整數int or=5來記住它,並且給它加一個指向目前所處位置的右括弧處:

  

 

  然后棧A輸出一個元素:第1階段的(,給它加一個指向or+1位置的方向:(如果有多個or,則分別指向各個or+1位置)

  

 

 

  然后遇見的是第10階段的),給它加一個指向下一個階段的方向,且棧A輸出數個元素,直到見到左括弧為止:

  

 

  然后棧A輸出一個元素:第0階段的(,中途沒見到 |, 因此不作操作:

  

 

  然后去到第11階段,構建完畢。

代碼實現:

  根據正則表達式構建有向圖:

  

  圖中紅色箭頭處,它默認了棧下一個吐出來的是左括號,但如果吐出來的是 | ,則會出錯,這里應該做一個針對連續吐出多個 | 的處理(加個判斷)。

  這段代碼只處理了符號,應該添加代碼給字符所處的階段指向下一個階段。

  使用這個有向圖來尋找字符串:

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM