具體可分為:字首(prefix)法和next陣列法
1. 思路:
1)求出字首(prefix)表 2)依照字首(prefix)表來幫助匹配查詢
所以,找出字首(prefix)表是最核心的步驟
2. 方法:
給出兩種求字首(prefix)表的方法,這兩種方法的核心大致相當,都利用回溯的思想
本例中用指標j進行回溯,所以我把它倆都稱之為:j值回溯法,來計算字首(prefix)表
1)求未成熟的字首(prefix)表,將未成熟的表處理為成熟的字首(prefix)表,加工處理過程為:prefix陣列每個元素都後移一位(最後乙個元素理所應當被覆蓋),再將prefix[0]置為-1即可
以str = "ababax"為例:
字首(prefix)表,**如下:
void
prefixtable
(void
)//字首表函式
else}}
//未成熟字首表求值結束,接下來為處理環節
for(i = n -
1; i >
0; i--
) prefix[i]
= prefix[i -1]
; prefix[0]
=-1;
//處理完畢,輸出成熟字首表
for(i =
0; i < n; i++
)printf
("%2d "
, prefix[i]);
return
;}
2)方法1)改進,改變初始指標的位置,改變prefix[0]的值,使之從1)中的0變成2)中的-1
void
prefix_table
(void
)else
j = prefix[j]
;//若字元不相同,則j值回溯(正下)
}//結束成熟字首表的求值,因為是成熟的,不需要加工,接下來列印成熟字首表
for(i =
0; i < n; i++
)printf
("%2d "
, prefix[i]);
return
;}
兩個方法大致相當,只不過回溯略有區別,1)中回溯為左下回溯,2)中回溯為正下回溯,解釋一下:
方法1):
下標:0 1 2 3 4 5 初始 i = 1, j = 0
字元:a b a b a x
prefix: 0 ? ?
prefix[0]初始化為0定義的時候,判斷str[i]與str[j]是否相等,如果相等,那麼立馬就能將prefix[1]填入相應的值,然後 i 後移,再次判斷,倘若str[i] 與str[j]不相等,就需要回溯j的值,j = prefix[j-1],意思就是,暫時得不到prefix[i]的值,把j所指元素的前乙個的prefix值即prefix[j-1]賦給j,完成回溯,由於j和prefix[j-1]的位置關係(如圖prefix[j-1]在j的左下位置),所以稱其為左下回溯法
方法2):
下標:-1 0 1 2 3 4 5 初始 i = 0, j = -1
字元:空 a b a b a x
prefix: -1 ? ?
prefix[0]初始化為-1,判斷if (j == -1 || str[i] == str[j]),str[-1]不存在,但是前面 j == -1成立時,後面就不計算了,所以無影響。這次如果if條件為假,那麼將j回溯, j = prefix[j],由於j和prefix[j]的位置關係(如圖prefix[j]在j的正下方),所以稱之為正下回溯法
下面給出完全**:
void
kmpsearch
(void
)else}}
for(i = n -
1; i >
0; i--
) prefix[i]
= prefix[i -1]
; prefix[0]
=-1;
for(i =
0; i < n; i++
)printf
("%2d "
, prefix[i]);
printf
("\n");
//以上部分可完全用方法2)求字首表來替換
j =0; i =0;
while
(i < m)
if(text[i]
== str[j]
) i++
, j++
;else
}for
(i =
0; i < n; i++
)printf
("%2d "
, prefix[i]);
return
;}
總結:方法1)和方法2)由於指標i、j初始位置不同,prefix[0]不同,回溯方式不同,得到的字首(prefix)表不同,方法2)直接一步算出,方法1)先算出未成熟的字首(prefix)表,再加工而得到成熟的字首(prefix)表,得到字首(prefix)表後,按照上述操作可進行匹配查詢
1. 思路:
1)求出next陣列 2)利用next陣列進行匹配查詢
2. 方法:
1)這裡的next陣列,實際上數值對應關係上為prefix陣列元素+1,舉個例子:prefix: -1 0 0 1 1 2 0 1,那麼next為:0 1 1 2 2 3 1 2,記住一點,對應關係為next[i]=prefix[i-1]+1,i>=1 直接利用上面的**(回頭看)進行+1操作
當然有更直接的方法:調節i,j指標,和字元陣列存放位置,這種方法就是上面方法2)求字首(prefix)表的改版,下面講一講具體操作:
2)初始i、j指標的位置:i = 1, j = 0; 因此字元在陣列中的存放應該是這樣,以str = "ababax"為例:
str : 0 1 2 3 4 5 6
字元 無 a b a b a x
str[0]不放字元,它只是乙個用來回溯的出發點設定(j = 0),像程杰老師著的《大話資料結構》一書中,就把字串的長度放在了str[0]中(原書是t[0]),所以**即為上面方法2)的小改版:
void
nextproces
(void
)else
j = next[j]
;//若字元不相同,則j值回溯
}for
(i =
1; i < n; i++
)printf
("%2d "
, next[i]);
return
;}
注:這種直接算next陣列的方法和prefix+1算next的方法結果略有不同:主要是位置不一樣,直接算next陣列結果從next[1]next[6]有的6個數,而j用prefix+1算next是從next[0]next[5]的6個數
接下來,就是用next陣列進行匹配查詢
查詢過程中,需要用到兩個指標,分別是i和j,i指標為主串指標,j指標為子串指標,通過子串j值回溯,進行匹配查詢,下面講一講next陣列進行匹配查詢的核心思路:
next陣列與(成熟)字首(prefix)表最大的區別有兩點,也是我反覆說的:
1. next陣列第一位即next[0]不存放有效數字,實際上是從1開始,子串第一位str[0]也不存放有效字元,比如對於str = "ababax"和next,實際上應該寫為str = 「xababax」,因此得到的next陣列也是從1開始,而字首(prefix)表從0開始
2. 數字關係上,有效數字對應為:next = prefix + 1,不考慮有效性(下標)的情況下,為next[i] = prefix[i-1]+1,i從1開始
注:next陣列法,在程杰所著《大話資料結構》中,子串str(書中為t串)和主串text(書中為s串),均是陣列下標從1開始的
程杰《資料結構》版總體**如下(有改動):
void
kmpsearch
(void
)else
j = next[j]
;//若字元不相同,則j值回溯
} j =
1, i =0;
//等價於j = i = 1;
while
(i <= m)
if(j ==
0|| text[i]
== str[j]
)else
j = next[j];}
return
;}
下面聊一聊我的想法,我認為陣列下標從1開始,不符合正常邏輯,所以我們還是將str陣列、text陣列下標從0開始,得到的next陣列也從0開始存放值,由於字首(prefix)表與next陣列特殊關係,所以我用字首(prefix)表改出了與上面新next陣列,與原next陣列不同的是,該新next陣列從0開始存放數值,下面給出我的**:
void
kmp_search
(void
)else
j = next[j]
;//若字元不相同,則j值回溯(正下)
}for
(i =
0; i < n; i ++
) next[i]+=
1;while
(i < m)
if(text[i]
== str[j]
)++i,
++j;
else
}return
;}
以上就是全部內容了,我表達意思可能不到位,多多包涵~~謝謝你的**! 串的兩種模式匹配演算法
靜時亦覺意思好,才遇事便不同,如何?是徒知靜養而不用克己工夫也。如此,臨事便要傾倒。人須在事上磨,方立得住,方能 靜亦定,動亦定。此時正宜用功。若此時放過,閒時講學何用?人正要在此等時磨鍊。子串的定位操作 串的模式匹配 挨個遍歷,例如在asdfghjkl中尋找dfg,需要將dfg與asdfghjkl...
模式匹配 KMP演算法
字串匹配演算法 include includeusing namespace std define ok 1 define error 0 define overflow 2 typedef int status define maxstrlen 255 使用者可在255以內定義最長串長 typed...
模式匹配KMP演算法
前些日子在為目前該學習什麼而苦惱,就問了一下已經從事多年軟體開發的表哥,他說乙個程式設計師要走的遠,就要學好資料結構和演算法,於是我就重新開始學習資料結構和演算法了 拿起以前上過的資料結構看,看到第四章串的模式匹配時,頗感興趣,就寫了一下程式,實踐了一下。感覺還蠻爽,於是就把以下幾個重要的函式放在此...