字串匹配的Sunday演算法--效能上超過KMP和BM演算法
阿新 • • 發佈:2019-01-27
第一次聽到Sunday演算法,是大餅餅說的。在他圖文並茂的解釋中,我發現這個演算法果然是一個又容易理解,效率又強過kmp和BM的演算法。
Sunday的移動次數更少!
於是試著寫了一個,果真是好東東,分享一下。
轉一些概念先:
Sunday演算法是Daniel M.Sunday於1990年提出的一種比BM演算法搜尋速度更快的演算法。其核心思想是:在匹配過程中,模式串並不被要求一定要按從左向右進行比較還是從右向左進行比較,它在發現不匹配時,演算法能跳過儘可能多的字元以進行下一步的匹配,從而提高了匹配效率。
假設在發生不匹配時S[i]≠T[j],1≤i≤N,1≤j≤M。此時已經匹配的部分為u,並假設字串u的長度為L。如圖1。明顯的,S[L+i+1]肯定要參加下一輪的匹配,並且T[M]至少要移動到這個位置(即模式串T至少向右移動一個字元的位置)。
圖1 Sunday演算法不匹配的情況
分如下兩種情況:
(1) S[L+i+1]在模式串T中沒有出現。這個時候模式串T[0]移動到S[T+i+1]之後的字元的位置。如圖2。
圖2 Sunday演算法移動的第1種情況
(2)S[L+i+1]在模式串中出現。這裡S[L+i+1]從模式串T的右側,即按T[M-1]、T[M-2]、…T[0]的次序查詢。如果發現S[L+i+1]和T中的某個字元相同,則記下這個位置,記為k,1≤k≤M,且T[k]=S[L+i+1]。此時,應該把模式串T向右移動M-k個字元的位置,即移動到T[k]和S[L+i+1]對齊的位置。如圖3。
圖3 Sunday演算法移動的第2種情況
依次類推,如果完全匹配了,則匹配成功;否則,再進行下一輪的移動,直到主串S的最右端結束。該演算法最壞情況下的時間複雜度為O(N*M)。對於短模式串的匹配問題,該演算法執行速度較快。
Sunday演算法JAVA程式碼:
- package math;
- import java.util.ArrayList;
- import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
- publicclass Sunday
- {
- public Sunday(){
- //
- }
-
public
- int str_length = 0;
- int fin_length = 0;
- int find_count = 0;
- int start = 0;
- int moveNum = 0;
- ArrayList<Integer> index = new ArrayList<Integer>();
- if(Sfind.length() == 0 || str.length() == 0){
- returnnull;
- }
- if (str.length() < Sfind.length()){
- returnnull;
- }
- str_length = str.length();
- fin_length = Sfind.length();
- while (start + fin_length <= str_length)
- {
- moveNum++;
- boolean isfind = false;// 是否在這次移動中找到
- String s_temp = str.substring(start, start + fin_length);
- if (Sfind.equals(s_temp)) {
- index.add(start);//記錄匹配字元的位置的第一個字元座標
- find_count++;
- start = start + fin_length;
- isfind = true;
- }
- if (isfind == false)// 如果沒找到計算下次移動位置
- {
- int forwardPos = QfindPos(str, Sfind, start, fin_length);
- start = forwardPos;
- }
- }
- System.out.println("move_count = " + moveNum);//移動次數顯著減少
- return index;
- }
- //找字元在字串(不算最後一個字元)的位置(倒數)
- //沒找到返回fin_length,找到返回位置
- /// 找字元在字串(不算最後一個字元)的位置(倒數);沒找到返回str.length,找到返回位置
- publicint QfindPos(String str, String find, int pos, int fin_length){
- int returnPos = str.length();
- char[] Schr = str.toCharArray();
- char[] Sfin = find.toCharArray();
- if ((pos + fin_length) < str.length())
- {
- char chrFind = Schr[pos + fin_length];//要找的字元
- if (fin_length >= 1)
- {
- if (find.lastIndexOf(chrFind) > -1)//如果find裡存在chrFind字元
- {
- returnPos = pos + fin_length - find.lastIndexOf(chrFind);
- }
- else{//如果find裡不存在chrFind字元
- returnPos = pos + fin_length + 1;
- }
- }
- }
- return returnPos;
- }
- publicstaticvoid main(String[] args) {
- String str = "return1111chrFind1Pos = chrFind1 pos + fin_length -returnPos = pos + fin_length -returnPos"
- + " = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos "
- + "+ fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length"
- + " -returnPos = pos + fin_length -returnPos = pos + fin_length - find.lastIndexOf(chrFind);returnPos = pos "
- + "+ fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length "
- + "-returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos"
- + " + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -"
- + " find.lastIndexOf(chrFind);returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos +"
- + " fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length "
- + "-returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos + fin_length -returnPos = pos "
- + "+ fin_length -returnPos = pos + fin_length - find.lastIndexOf(chrFind1)";
- String find = "chrFind1";
- Sunday sunday = new Sunday();
- ArrayList<Integer> index = sunday.QfindChr(str, find);
- if(index == null){
- System.out.println("條件不符合");
- return;
- }
- System.out.println("count = "+index.size());
- for (int i = 0; i < index.size(); i++) {
- System.out.print("index:"+index.get(i)+", ");
- }
- }
- }