【轉載】KMP入門級別算法詳解--終於解決了(next數組詳解)

【轉載】https://blog.csdn.net/LEE18254290736/article/details/77278769算法

對於正常的字符串模式匹配,主串長度爲m,子串爲n,時間複雜度會到達O(m*n),而若是用KMP算法,複雜度將會減小線型時間O(m+n)。數組

 

設主串爲ptr="ababaaababaa";,要比較的子串爲a=「aab」;spa

 

KMP算法用到了next數組,而後利用next數組的值來提升匹配速度,我首先講一下next數組怎麼求,以後再講匹配方式。.net

 

next數組詳解blog

首先是理解KMP算法的第一個難關是next數組每一個值的肯定,這個問題困惱我很長時間,尤爲是對照着代碼一行一行分析,很容易把本身繞進去。ip

定義一串字符串字符串

ptr = "ababaaababaa";get

 

next[i](i從1開始算)表明着,除去第i個數,在一個字符串裏面從第一個數到第(i-1)字符串前綴與後綴最長重複的個數。string

 

什麼是前綴?it

在「aba」中,前綴就是「ab」,除去最後一個字符的剩餘字符串。

同理能夠理解後綴。除去第一個字符的後面所有的字符串。

 

在「aba」中,前綴是「ab」,後綴是「ba」,那麼二者最長的子串就是「a」;

在「ababa」中,前綴是「abab」,後綴是「baba」,兩者最長重複子串是「aba」;

在「abcabcdabc」中,前綴是「abcabcdab」,後綴是「bcabcdabc」,兩者最長重複的子串是「abc」;

 

這裏有一點要注意,前綴必需要從頭開始算,後綴要從最後一個數開始算,中間截一段相同字符串是不行的。

 

再回到next[i]的定義,對於字符串ptr = "ababaaababaa";

next[1] = -1,表明着除了第一個元素,以前前綴後綴最長的重複子串,這裏是空 ,即"",沒有,咱們記爲-1,表明空。(0表明1位相同,1表明兩位相同,依次累加)。

next[2] = -1,即「a」,沒有前綴與後綴,故最長重複的子串是空,值爲-1;

next[3] = -1,即「ab」,前綴是「a」,後綴是「b」,最長重複的子串「」;

next[4] = 1,即"aba",前綴是「ab」,後綴是「ba」,最長重複的子串「a」;next數組裏面就是最長重複子串字符串的個數

next[5] = 2,即"abab",前綴是「aba」,後綴是「bab」,最長重複的子串「ab」;

next[6] = 3,即"ababa",前綴是「abab」,後綴是「baba」,最長重複的子串「aba」;

next[7] = 1,即"ababaa",前綴是「ababa」,後綴是「babaa」,最長重複的子串「a」;

next[8] = 1,即"ababaaa",前綴是「ababaa」,後綴是「babaaa」,最長重複的子串「a」;

next[9] = 2,即"ababaaab",前綴是「ababaaa」,後綴是「babaaab」,最長重複的子串「ab」;

next[10] = 3,即"ababaaaba",前綴是「ababaaab」,後綴是「babaaaba」,最長重複的子串「aba」;

next[11] = 4,即"ababaaabab",前綴是「ababaaaba」,後綴是「babaaabab」,最長重複的子串「abab」;

next[12] = 5,即"ababaaababa",前綴是「ababaaabab」,後綴是「babaaaababa」,最長重複的子串「ababa」;

 

還有另一種方法,我看的有的書上寫着:

這裏咱們定義next[1] = 0 , next[1] = 1;

 

再分析ptr字符串,ptr = "ababaaababaa";

跟上一個的狀況相似,

 

next[1] = 0 ,事先定義好的

next[2] = 1 ,事先定義好的

next[3] = 1 ,最長重複的子串「」;1表明沒有重複,2表明有一個字符重複。

next[4] = 2 ,最長重複的子串「a」;追償的長度加1,即爲2.

next[5] = 3 ,如下都跟以前的同樣,這種方法是最長的長度再加上一就能夠了。

next[6] = 4

next[7] = 2

next[8] = 2

next[9] = 3 

next[10] = 4 

next[11] = 5 

next[12] = 6 

 

以上是next數組的詳細解釋。next數組求值 是比較麻煩的,剩下的匹配方式就很簡單了。

next數組用於子串身上,根據上面的原理,咱們可以推出子串a=「aab」的next數組的值分別爲0,1,2.(按照我說的第二種方式算的)。

 

首先開始計算主串與子串的字符,設置主串用i來表示,子串用j來表示,若是ptr[i]與a[i]相等,那麼i與j就都加1:

prt[1]與a[1]相等,i++,j++:

用代碼實現就是

 

[cpp]  view plain  copy
 
  1. if( j==0 ||  ptr[i]==a[j])  
  2. {  
  3.     ++i;  
  4.     ++j;  
  5. }  

 

 

ptr[2]與a[2]不相等

此時ptr[2]!=a[2],那麼令j = next[j],此時j=2,那麼next[j] = next[2] = 1.那麼此時j就等於1.這一段判斷用代碼解釋的話就是:

 

[cpp]  view plain  copy
 
  1. if( ptr[i]!=a[j])  
  2. {  
  3.       j = next[j];  
  4. }  

加上上面的代碼進行組合:

在對兩個數組進行比對時,各自的i,j取值代碼:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">while( i<ptr.length && j< a.length)  
  2. {  
  3.      if( j==0 || ptr[i]==a[i] )  
  4.     {  
  5.           ++i;  
  6.           ++j;</span>  
[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">          next[i] = j;  
  2.     }  
  3.     else  
  4.     {  
  5.           j = next[j];  
  6.     }  
  7. }</span>  



 

 

此時將a[j]置於j此時所處的位置,即a[1]放到j=2處,由於在j=2時出現不匹配的狀況。

 

此時再次計算是否匹配,能夠看出來a[1]!=ptr[2],那麼j = next[j],即此時j = next[1] = 0;

根據上面的代碼,當j=0時,執行++i;++j;

此時就變爲:

此時ptr[3] = a[1],繼續向下走,下一個又不相等了,而後「aab」向後挪一位,這裏再也不贅述了,主要的思想已經講明白了。到最後一直到i = 8,j=3時匹配成功,KMP算法結束。整個過程就結束了。

相關文章
相關標籤/搜索