【算法】字符串匹配之Z算法


求文本與單模式串匹配,通常會使用KMP算法。后來接觸到了Z算法,感覺Z算法也相當精妙。在以前的博文中也有過用Z算法來解決字符串匹配的題目。

下面介紹一下Z算法。

先一句話講清楚Z算法能求什么東西。

輸入為一個字符串s,Z算法可以求出這個字符串每一個后綴與自身的最長公共前綴LCP,Z算法可以求出一個數組z,z[i]表示suffix(i)與字符串本身的最長公共前綴。

接下來,介紹Z算法的具體內容。

記字符串s的長度為n。

Z算法需要維護一對值,記為left和right,簡記為L和R。L和R滿足s[L,R]為s串的前綴。當i為1的時候,暴力比較s[0,n-1]與s[1,n-1]可得此時的L與R,同時也得到了z[1],即suffix(1)與s本身的LCP。

假設計算至i-1,我們已經得到了當前的L與R,同時也得到了z[1]到z[i-1]的值,現在需要計算z[i]與新的L和R。

1.假設i>R,則說明不存在一個結束於i或者i之后的串,同時這個串本身也為s的一個前綴,否則R不應該小於i。對於這種情況,需要重新計算新的L與R,令L=R=i,暴力比較s與suffix(i),得到z[i]=R-i+1=R-L+1。

2.此時i<=R,令k=i-L,可以斷言z[i]>=min(z[k],R-i+1)。因為根據L與R的含義,此時我們可以將L到R視作為字符串的前綴,那么i相對於L的偏移量為k。

如果z[k]<R-i+1,則z[i]必然等於z[k],基於此時,s[k,k+z[k]-1]是s[i,R]的一個前綴,同時在這種情況下L與R不變。

如果z[k]>=R-i+1,根據R的含義可知s[R+1]!=s[R-L+1],z[k]中大於R-i+1的匹配信息因為s[R+1]!=s[R-L+1]而無效,但這並不意味着s[R+1]!=s[R-i+1],此時根據z[k]可以斷言z[i]至少是R-i+1,是否可以更大需要再進行計算,令L=i,更新R值,並得到此時的z[i]。

 

具體實現中,第二種情況的兩種子情況可以歸一化處理。

 

給出一個C++實現代碼:

 1 void Z(char *s,int n=0) {
 2     n=(n==0)?strlen(s):n;
 3     z[0]=n;
 4     int l=0,r=0;
 5     for (int i=1;i<n;i++) {
 6         if (i>r) {
 7             l=i,r=i;
 8             while (r<n&&s[r-i]==s[r]) r++;
 9             z[i]=r-l;
10             r--;
11         }
12         else {
13             int k=i-l;
14             if (z[k]<r-i+1)
15                 z[i]=z[k];
16             else {
17                 l=i;
18                 while (r<n&&s[r-i]==s[r]) r++;
19                 z[i]=r-l;
20                 r--;
21             }
22         }
23     }
24 }
View Code

 

過程中每個字符至多被L和R掃到一次,Z算法是線性的復雜度。

Z算法解決單模式串匹配的方法很簡單,令S為文本串,T為模式串,構造新串P=T+'#'+S,計算z數組,從S在P中開始的位置向后掃描,如果z[i]=length(S),則說明此處有一個匹配。當然其實也可以不加'#',那樣子的話判斷需要用>=而不是=。相較於KMP,對於解決單模式串匹配,如求所有匹配位置,其實用Z算法求的話是可以做到不用額外空間的(對於字符串拼接,可以不用開一個新的字符串。過程中判斷匹配即可,不必保存z數組。),而KMP的話,不保存模式串的next數組,是沒辦法進行匹配運算的。

 


免責聲明!

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



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