串是由零個或多個字符組成的有限序列,又叫作字符串javascript
串的邏輯結構和線性表很類似的,不一樣的是串針對是是字符集,因此在操做上與線性表仍是有很大區別的。線性表更關注的是單個元素的操做CURD,串則是關注查找子串的位置,替換等操做。html
固然不一樣的高級語言對串的基本操做都有不一樣的定義方法,可是總的來講操做的本質都是類似的。好比javascrript查找就是indexOf, 去空白就是trim,轉化大小寫toLowerCase/toUpperCase等等java
這裏主要討論下字符串模式匹配的幾種經典的算法:BF、BM、KMPgit
BF(Brute Force)算法github
Brute-Force算法的基本思想:算法
從目標串s 的第一個字符起和模式串t的第一個字符進行比較,若相等,則繼續逐個比較後續字符,不然從串s 的第二個字符起再從新和串t進行比較。express
依此類推,直至串t 中的每一個字符依次和串s的一個連續的字符序列相等,則稱模式匹配成功,此時串t的第一個字符在串s 中的位置就是t 在s中的位置,不然模式匹配不成功app
可見BF算法是一種暴力算法,又稱爲樸素匹配算法或蠻力算法。ide
主串 BBC ABB ABCF性能
子串 ABC
在主串中找出子串的位置,對應了其實就是javascript的indexOf查找方法的實現了
var sourceStr = "BBC ABB ABCF"; var searchStr = "ABC";
function BF_Ordinary(sourceStr, searchStr) { var sourceLength = sourceStr.length; var searchLength = searchStr.length; var padding = sourceLength - searchLength; //循環的次數 //BBC ABB ABCF =>ABC => 搜索9次 for (var i = 0; i <= padding; i++) { //若是知足了第一個charAt是相等的 //開始子循環檢測 //其中sourceStr的取值是須要疊加i的值 if (sourceStr.charAt(i) == searchStr.charAt(0)) { //匹配成功的數據 var complete = searchLength; for (var j = 0; j < searchLength; j++) { if (sourceStr.charAt(i + j) == searchStr.charAt(j)) { --complete if (!complete) { return i; } } } } } return -1; }
BF算法就是簡單粗暴,直接把BBC ABB ABCF母串的每個字符的下表取出來與模式串的第一個字符匹配,若是相等就進去字串的再次匹配
這裏值得注意:
1:最外圍循環的次數sourceLength - searchLength,由於咱們匹配的母串至少要大於等於子串
2:在子串的繼續匹配中,母串的起點是須要疊加的(i+j)
3:經過一個條件判斷是否徹底匹配complete,BBC ABB ABCF中,咱們在ABB的時候就須要跳過去
上面是最簡單的一個算法了,代碼上還有更優的處理,好比在自串的匹配上能夠採起取反的算法
優化算法(一)
function BF_Optimize(sourceStr, searchStr) { var mainLength = sourceStr.length; var searchLength = searchStr.length; var padding = mainLength - searchLength; for (var offset = 0; offset <= padding; offset++) { var match = true; for (var i = 0; i < searchLength; i++) { //取反,若是隻要不相等 if (searchStr.charAt(i) !== sourceStr.charAt(offset + i)) { match = false; break; } } if (match) return offset; } return -1; }
咱們不須要判斷爲真的狀況,咱們只要判斷爲假的狀況就能夠了,當子匹配結束後match沒有被修改過的話,則說明此匹配是徹底匹配
以上2種方法咱們都用到了子循環,咱們可否改爲一個循環體呢?
其實咱們能夠看到規律,主串每次都只會遞增+1,子串每次匹配也是從頭開始匹配,因此咱們能夠改爲一個while,控制下標指針就能夠了
優化算法(二)
function BF_Optimize_2(sourceStr, searchStr) { var i = 0, j = 0; while (i < sourceStr.length) { // 兩字母相等則繼續 if (sourceStr.charAt(i) == searchStr.charAt(j)) { i++; j++; } else { // 兩字母不等則角標後退從新開始匹配 i = i - j + 1; // i 回退到上次匹配首位的下一位 j = 0; // j 回退到子串的首位 } if (j == searchStr.length) { return i - j; } } }
i就是主串的下標定位,j就是子串的下標定位
當主串子串相等的時候,就進入了子串的循環模式,當子循環的次數j知足子串長度時,就驗證是徹底匹配
當主串子串不相等的時候,就須要把主串的下標日後移一位,固然i的時候,由於可能通過子串的處理,因此須要i-j+1, 而後復位子串
具體咱們能夠看看代碼比較
基於BF算法的四種結構,for/while/遞歸
BF也是經典的前綴匹配算法,前綴還包括KMP,咱們可見這種算法最大缺點就是字符匹配失敗指針就要回溯,因此性能很低,以後會寫一下KMP與BM算法針對BF的的升級