字串查詢演算法總結及MS的strstr原始碼

2021-06-23 06:34:55 字數 3484 閱讀 1266

首先來說說字串的查詢,即就是在乙個指定的字串a中查詢乙個指定字串b出現的位置或者統計其他b在a中出現的次數等等相關查詢。

ms自己提供了乙個strstr函式原型:extern char *strstr(char *str1, char *str2);標頭檔案。但也可不包含標頭檔案直接使用下面**: 

char * __cdecl strstr (

const

char * str1,

const

char * str2

)return(null);

}

可直接將char 替換為 wchar_t用於寬字元。不過在字串的查詢中效率是最低。

kmp演算法 詳細介紹可參考下面:1、kmp演算法   2、kmp演算法詳解

其實kmp演算法可對裡面的陣列然後會更優化一些。

sunday 是bm查詢演算法的變種,但是效率更好。下面摘自園友aga.j的文章:

sunday演算法是一種比kmp和bm更加高效的匹配演算法,它的思想跟bm演算法相似,在匹配進行時,sunday演算法失敗時關注的是源字串中當前參加匹配的長度為m(模式串的長度)的最後一位字元的下乙個字元。如果該字元不在模式串中出現,那麼就將直接跳過,即移動步長=模式串的長度+1,否則,則移動步長=模式串中最右端的該字元到末尾的距離加1。

假設我們要匹配」hereisa******example」和」example」

text:here  is  a  ****** example(此處中間是乙個空格,用兩個空格是為了對齊.下同)

pattern:example

我們從源串的初始位置開始和模式串比較,發現第乙個就不匹配了,這時候如果使用樸素演算法,那麼我們就只是將模式串右移一位,而使用kmp演算法的話,則根據自身的模式陣列來確定移動的步長,使用sunday演算法時,我們會比較源串對齊後的下乙個字元,也就是text中is後面的空格,因為無論我們用什麼方法去移動模式串,這個字元總是要參與下一次匹配(假設我們只移動1位或者小於模式串長度位,這個空格所在的位一定要參與匹配,不然我們就可能遺漏掉可能的匹配,而假設在小於模式串長度的位的移動中沒有匹配,那麼下乙個起始匹配點就是空格的所在位)。

既然知道該為一定要匹配,那麼就和模式串進行比較,如果模式串中不存在該字元(這裡是空格),那麼就直接跳到空格字元的下乙個字元進行匹配(這時候從e和a開始匹配)

text:here  is  a  ******  example

pattern:example

這時候再次進行匹配的判斷,發現第乙個字元又不匹配,所以和上面的方法一樣,我們直接看對齊後的下乙個字元e,這時候拿e從右往左和模式串比較,如果模式串中存在e,那麼就移動模式串知道兩個e對齊,這樣得到(這種從後往前,從右往左的匹配思想是從bm演算法借鑑過來的,從後往前比較的優點是一旦遇到不匹配的時候,可以跳躍的距離更大,因為後面都不匹配了,前面再匹配都沒有用了,所以這裡拿e從模式串中從右往左匹配,找到第乙個和e匹配的位置—詳細看bm演算法的分析!)

text:here  is  a  ******  example

pattern:example

接下來還是從模式串的首位和源串的新起始位開始比較,發現,又無法匹配,而再判斷對齊的後一位,這次還是將整個模式串移動到新的不匹配位空格之後,最後完成匹配。

上述例子是從網上摘錄下來的,不是很典型,因為在第二次匹配的時候,剛好源串對齊後的下一位就和模式串的最後一位匹配,所以體現不出sunday演算法的模式串移動的過程。

/*

* 演算法分析:

* 1 從第乙個字元開始,對pattern和source逐個字元比較

* 2 如果出現匹配失敗,則檢查source的在pattern末尾位置的後乙個字元是否和pattenr的某個乙個字元相等

* 如果是,則對其到那個相等字元(從右往左對齊),重新執行

* 如果不是,則將pattern和source的比較初始位設定在source的後後個字元,再執行

* 3 如果匹配,則成功

*/#includeusing

namespace std;

//呼叫前先檢測源串是否超過長度,函式返回 以源串的sourcestartpos為比較的起始點,第乙個和模式串不匹配的字元的位置,

int compare(char* source, char* pattern, int sourcestartpos, int patternlength)

return i; //

返回pattern中無法匹配的元素的位置i,pattern[i]

}bool sundaymatch(char* source, char* pattern, int sourcelength, int patternlength)

if(j < 0) //

如果不存在,則跳過

startpos = startpos+patternlength + 1;

}cout<

newstartpos

"false;

}void main()

網路上的實現方法是這樣的,先做乙個預處理,針對子串中每個出現的字元,儲存源串對齊後的下一位一旦出現匹配或者不匹配所需要移動的距離,然後在匹配過程中就可以直接使用,不需要像我寫的方法那樣重複的判斷某個位的字元是否和模式串中出現及其出現位置是**。(這種方法花費了空間但贏得了時間)

/*

採用bm/kmp的預處理的做法,事先計算好移動步長,等到遇到不匹配的值直接使用

*/#include #include

using

namespace std;

#define max_char_size 256 //

乙個字元8位 最大256種

/** 設定每個字元最右移動步長,儲存每個字元的移動步長

* 如果大串中匹配字元的右側乙個字元沒在子串中,大串移動步長=整個串的距離+1

* 如果大串中匹配範圍內的右側乙個字元在子串中,大串移動距離=子串長度-這個字元在子串中的位置

*/int *setcharstep(char *substr)

return charstep;}/*

* 演算法核心思想,從左向右匹配,遇到不匹配的看大串中匹配範圍之外的右側第乙個字元在小串中的最右位置

* 根據事先計算好的移動步長移動大串指標,直到匹配

*/int sundaysearch(char* mainstr,char* substr,int* charstep)

else

}if(sub_j == substrlen)

return (main_i - substrlen);

}return -1;

}int main()

【參考資料 感謝作者】

以上部分摘自: 字串匹配演算法之sunday演算法的學習筆記

另外例舉幾篇關於字串查詢的文章:

1、kmp演算法

2、kmp 演算法並非字串查詢的優化 ***

3、精確字串匹配(bm演算法) ***

4、sunday演算法簡介

字元及字串查詢函式總結

相關函式 index,rindex,strchr,strpbrk,strrchr,strsep,strspn,strstr memchr 1 index 函式 首先說明 index 函式是linux下的字串函式,不是標準庫的,所以在vc 6.0上是使用不了的。表頭定義 include 函式定義 ch...

字串查詢 1 暴力字串查詢演算法

virtual int findstr const string haystack,const string needle override if j patsize return i return 1 最差情況下,haystack可能是 aaa.aaa needle是 a.ab 在這種情況下,需要...

字串 演算法總結

1.字串的複製 char scopy char str1,const char str2 int main char scopy char str1,const char str2 str1 j 0 return str1 2.字串的回文 判斷字串是否是回文 include include usin...