字串的特徵向量與KMP演算法

2021-06-29 11:20:30 字數 3352 閱讀 4920

字串的特徵向量就是由字串各位置上的特徵數構成的乙個向量。設字串為p,令pi為從字串首字母到第i個位置的字首,則字串p的i位置上的特徵數就是pi的首尾非空真子串匹配的最大長度。例如:字串abcdaabcab的特徵向量是(0,0,0,0,1,1,2,3,1,2)。其中第5個位置的特徵數是1,因為p5是abcdaa,首尾非空真子串能夠匹配的就是a;而第7個位置的特徵數是3,因為p7是abcdaabc,首尾非空真子串能夠匹配的是abc。0位置上的特徵數顯然為0。暴力法求特徵向量顯然是非常耗時的,實際上可以基於這樣乙個思路求特徵向量:從0位置開始求特徵數,i位置上的特徵數是由之前位置的特徵數決定的。

用next陣列表示字串的特徵向量。如上所示,假設next[i-1]==x1,也就是說i-1位置上首尾x1個元素是匹配的。很顯然,當p[i]==p[x1]時,i位置上首尾至少有x1+1個元素匹配,實際上就是x1+1個。

當p[i]!=p[x1]時,考查next[x1-1],假設其值為x2,這說明在x1-1位置上首尾x2個元素相等,此時,如果p[i]==p[x2],則說明i位置首尾至少有x2+1個元素是匹配的,實際上就是x2+1個。如果p[i]!=p[x2],則可以考查next[x2-1],依次類推。直到x的值為0,此時如果p[i]==p[x],則特徵數為x+1也就是1,否則就是0。 上述過程並不是演算法的充分條件,只是由特徵向量推出的性質;當然理論上也早有證明,這個性質是可以用來求出特徵向量的。

poj2406是一道利用特徵向量的簡單題目。假設字串s是另外乙個字串的t的n次方,即s是由t重複n次得到的,那麼最後乙個位置上的首尾匹配的真子串一定是s-t,而t的長度一定是s的長度減去s最後乙個位置上的特徵數,且s的長度一定是t長度的整數倍。這也是由n次方得到的性質,但是利用這個性質一樣可以證明s一定是t的n次方。

//令字串s=s^n,求最大的n

//例如aaaa = a^4 = (aa)^2,則答案是4

//如果s長度為l,s的長度為l,則s最後乙個特徵數一定是l-l

//而且l是l的n倍

#include

#include

using

namespace

std;

#define size 1000005

void getnext(char

const p,int n,int next)

return;

}char p[size];

int next[size];

int main()

return

0;}

令目標為t,模式為p,問p是否為t的子串,這就是字串匹配問題。暴力法很容易想到,每當不匹配的時候,將p後移乙個位置,再次嘗試匹配。kmp演算法的關鍵就在於當不匹配時,p是否只能往後移動一位?

上圖很明顯顯示了:當pi與對應的ti字母不相等時,p可以往後移多位。假設next[i-1]==2,則p應該往後移動i-2位再進行匹配。一般的,應該往後移動i-next[i-1]位。例如:abcdaabcab的特徵向量是(0000112312),假設t1是abcz….,則當比較到第3位的時候字母不匹配,此時應該把p後移3-next[2]也就是3位,再進行比較;假設t2是abcdaabcz…,則當比較到第8位的時候不匹配,此時應該將p後移到8-next[7]也就是5位,再開始比較。

不過,特別要注意的是:在上圖中,p後移了i-next[i-1]位以後(假設next[i-1]是2),p0還需要與t[i-2]進行比較嗎?p1還需要與t[i-1]進行比較嗎?不需要,因為可以確定是相等的,只需從ti和p2開始往下比較。所以很重要的一點:kmp演算法中,t的字母只訪問一次,t中已經比較過的字母不需要再次與p去比較,t的下標不存在回溯。所以kmp演算法是線性時間的。對p而言,當pi字母與t對應字母不匹配時,需要從p的第next[i-1]個字母重新開始比較。在上圖中,就是要從第2個字母開始比較。在t2的例子中,t2是abcdaabcz…,p是abcdaabcad,當第8個字母a與t的字母z不匹配的時候,我們只需從p中的第3個字母d開始再跟t比較。

所以,我們可以重新定義特徵數和特徵向量,i位置的特徵數就是當i位置的字母不匹配的時候,p的下標需要重新定位的位置。特別的當第0個位置不匹配時,令next[0]==-1,表示p的下標仍然維持在0,但t的下標要往後移一位。於是p: abcdaabcad的kmp匹配演算法的特徵向量是(-1,0,0,0,0,1,1,2,3,1)。

kmp演算法仍然有優化的可能,當t3是abcdaaz…時,比較到第6個字母,p的下標應該回溯到幾?按上述值應該是1,但其實可以回到0。假設t4是abcdz…,當比較到第4個字母不匹配時,p的下標應該回到幾?可以回到0,但實際上t的這個字母不用再比較了,所以在p的下標回到0的同時,t的下標應該加1。這種操作恰好是特徵向量為-1時應該進行的操作。

如上,令i的特徵數是x1,即當pi不匹配時,p下標應該回到x1。但是當p[i]==p[x1]時,下標還可以再往前。令x1位置的特徵數是x2,則顯然下標可以回到x2,當p[i]!=p[x2]時。否則,下標還可以往前,如此反覆直到0位置。如果p[i]==p[0],則i位置的特徵數應該是-1,表示此位置不匹配時,p的下標維持在0,而t的下標加1。字串abcdaabcab優化過後的特徵向量應該是(-1,0,0,0,-1,1,0,0,3,0)。

hdu1711是標準的kmp問題,只不過匹配的不是字串,而是整數序列。

//kmp演算法,匹配的不是字串行,而是整數

#include

using

namespace

std;

int t[1000005];

int p[10005];

int next[10005];

void getkmpnext(int

const p,int n,int next)

}//在t中搜尋p,輸出第乙個找到的位置,否則輸出-1

int kmp(int

const t,int tn,int

const p,int pn,int

const next)

return -1;

}int main()

return

0;}

字串匹配與KMP演算法

參考 j.boxer s blog the knuth morris pratt algorithm in my own words.在電腦科學中,knuth morris pratt字串查詢演算法 簡稱為kmp演算法 可在乙個主文字字串 s 內查詢乙個詞 w 的出現位置。此演算法通過運用對這個詞在...

字串 KMP演算法

而kmp演算法在字串匹配方法中乙個很著名並且很聰明的演算法,當然也確實比較難理解。甚至於有程式設計師因為無法理解kmp演算法而直接改用暴力匹配。本身自己學演算法起步較晚,第一次接觸到kmp演算法已經是研究生畢業一年了。雖然帶著研究生的學歷背景,但是剛開始看的時候依然是一臉懵逼。看了很多博主的講解總算...

字串 KMP演算法

而kmp演算法在字串匹配方法中乙個很著名並且很聰明的演算法,當然也確實比較難理解。甚至於有程式設計師因為無法理解kmp演算法而直接改用暴力匹配。本身自己學演算法起步較晚,第一次接觸到kmp演算法已經是研究生畢業一年了。雖然帶著研究生的學歷背景,但是剛開始看的時候依然是一臉懵逼。看了很多博主的講解總算...