對於串的匹配,較為簡單的有bf演算法,但這種方法的可用性卻較差。因為在每次不匹配的時候,主串(m位)和子串(n位)都會回溯,有一種最壞的情況就是,主串每前進一位,都在n次匹配後失敗然後回溯,如:
主串:aaaaaaaaaaaaaaaaaaaaab
子串:aab
這樣會導致bf演算法的時間複雜度大大提公升:
t = o((m-n)*n))≈ o(m*n) 由於 m串長度 >> n串長度 一般忽略
然而,在實際應用中,時間帶給使用者的體驗是最為重要的,一般都是不惜增加空間複雜度而來提高時間複雜度。
這時kmp演算法的出現,大大優化了bf演算法的這一缺陷。
kmp演算法是以d.e.knuth、j.h.morris和v.r.pratt共同命名的演算法,是他們3人在同一時期異途同歸的結果,故簡稱kmp演算法。
實質:是消除了主串的回溯,並且使得子串的指標是跳躍性的定位,大大提公升了演算法的效率。
想法:在bf演算法進行的過程中,若子串匹配到最後一位時發現不匹配,子串和主串同時回溯。這會使得無用功變了許多。但是,當子串和主串已經匹配了許多位時,意味著主串已匹配的部分已經被我們所知(即子串的部分),那何不對子串進行預先處理,在進行匹配時使得主串已匹配過的部分不再是無效的,如:
主串:aabaabaac
子串:aabaac
當主串匹配到五位匹配不成功時,對子串進行回溯:由於子串中該資料之前有相同的字首和字尾,即子串本身的開頭部分和中間部分能夠進行匹配,而此時在主串中已匹配的資料部分是和子串一樣的,即子串的開頭部分仍能匹配到主串的部分資料。
經過這樣的推導,使得這一想法的建立過程逐漸明確,也就是我們的kmp演算法。
在進行匹配的過程中,主串是一直進行遍歷,而子串需要進行回溯。問題是:子串是怎麼回溯的?
這就需要我們對子串的內容進行分析得到next陣列作為子串回溯的依據,當然這也是kmp演算法中最重要和最難的一點。
next實質:子串想要回溯,該回溯到哪兒。經過之前的想法,我們可以發現當子串的某位資料前出現了相同的字首和字尾時,也意味著此時子串的開頭部分和主串是能夠匹配的。所以next陣列的實質就是找到每個資料之前的相同字首和字尾長度,在匹配過程中能夠通過長度定位到相匹配的地方。
建立:此處我以串的格式進行建立:首位存放長度,之後儲存資料,陣列名a
①對於a[1]來說,匹配長度為0,則next[1] = 0(此處若陣列首位有值,next[0] = -1)
②定義乙個 i 來遍歷陣列,定義 j 來進行匹配測試同時也記錄匹配長度。
③i 從2開始向後遍歷
④ 對a[i] 和 a[j+1] 進行判斷(判斷已匹配的下一位,此時應該會理解初始值0和-1的原因)
⑤若不相等且未回溯到子串首位,對於 j 進行回溯(因為 i 之前的next陣列已經建立,這裡相當於kmp演算法中的子串指標進行跳躍)
⑥若相等,j++(匹配長度加一)
⑦next[i] = j (根據匹配結果對於next陣列進行賦值)
此時我們已經求得了next陣列,在匹配的過程中可以不像bf演算法中兩者都進行回溯,在處理過程中和求next陣列的方法類似(乙個是主串和子串,乙個是子串和子串):當匹配失敗時,對next陣列迴圈回溯,直至找到相同的部分或者
回溯到子串首部。
此處我寫了乙個案例進行測試:將檔案中的資料進行匯入以串的形式儲存,輸入子串進行匹配
①資料匯入函式
//資料節點
class strandnode
/*** 從檔案中讀取資料,構建主串(鏈式儲存)
* @param str 檔名
* @return 頭指標(未儲存資料)
*/public strandnode getmainstrand(string str)
//末尾置空
movenode.setnextnode(null);
} catch (exception e) finally
} catch (ioexception e)
}return headnode;
}
②根據子串求出next陣列
/**
* 根據子串計算出next陣列
* @param son 子串(首位為空)
* @return next陣列
*/public int analynext(char son)
//若兩者相等,則匹配長度加一
if(son[i] == son[j+1])
next[i] = j;
}return next;
}
③匹配過程
/**
* 進行匹配演算法
* @param headnode 主串頭指標
* @param son 子串
* @param next 根據子串建立的next陣列
* @return 子串在主串中的位置(未找到返回零)
*/public string strandmatched(strandnode headnode, char son, int next)
char mainstrand = sbuilder.tostring().tochararray();
//求取串長,提高每次判斷所用效率
int mainlength = mainstrand.length;
int sonlength = son.length;
//進行匹配
int j = 0; //子串的游標
for(int i=1; i④主函式
public static void main(string args) else
}
主串檔案(幻聽全文):
對於匹配位置的驗證:
(「 」並未讀取到串中)首段開頭18位
對於多次匹配的測試:
KMP演算法的簡單理解 筆記
這裡 kmp演算法是一種改進的 字串匹配 演算法,由d.e.knuth與v.r.pratt和j.h.morris同時發現,故稱kmp演算法 字串匹配 從字串t中尋找字串p出現的位置 p遠小於t 其中p稱為 模式 kmp演算法對模式串進行o m 的預處理後只需對文字t掃瞄一次即可找到匹配,所以時間複雜...
KMP演算法的理解
串的模式匹配演算法主要有兩種,一是簡單模式匹配,而是kmp演算法。簡單模式匹配演算法很容易理解,每一次從主串的第乙個位置起和模式串的第乙個字元開始比較,如果相等就按照順序一直比下去,如果不相等就把模式串和第二個位置開始繼續進行比較,最後若匹配成功則返回主串中與模式串匹配的第乙個字元的位置,雖然簡單易...
理解KMP演算法
由暴力匹配引入kmp演算法 問題 有乙個文字串s,和乙個模式串p,現在要查詢p在s中的位置。如果用暴力匹配的思路,並假設現在文字串s匹配到 i 位置,模式串p匹配到 j 位置,則有 示例 上面s,下面p 比如從a這裡開始匹配上了 一直這樣匹配下去 到這裡匹配不上了 就回滾回去重新開始 這種回滾的問題...