字符串匹配--Sunday算法
前面一篇博客詳細介紹了KMP算法,KMP算法的代碼不算繁瑣,但是理解起來相對比較困難。
后來Daniel M.Sunday在1990年提出了Sunday算法,其思想是從前往后匹配,在匹配失敗時關注的不是失配位而是主串中參加匹配的最末位字符的下一位字符。
舉個例子說明下Sunday算法
1)第一輪比較,S[2]!=P[2],發生失配,那么就關注主串中參與匹配的最末位字符的下一位,也就是S[3] (圖中綠色塊),因為e不存在于模式串P中,那么就將模式串移動4位(pLen+1)
因為移動不大于pLen位的時候,在匹配時都需要將S[3]與模式串某個字符進行匹配,肯定是無法匹配上的,畢竟S[3]不存在于模式串P中
2)第二輪比較,同樣的,S[6]!=P[2]發生失配,關注的字符就是S[7] “b”,它存在于模式串P中,移動模式串使得S[7]與該字符在模式串中最右的位置匹配上,即移動2位
3)第三輪就匹配上了
Sunday的思想比較簡單,就直接上代碼了
在Sunday算法中,我們關注主串中參與匹配的最末位字符的下一位(假設為X)是否存在于模式串中,根據其不同的結果移動的位數也不一樣, 想要算法效率高,那么我們就需要快速地得到某個字符X是否存在于模式串中,以及對應需要位移的長度。
所以X是否存在于P不適合通過contains這樣的函數來判斷,因為其底層是通過逐步比較的方式,時間復雜度為O(m),效率很差,那么有什么更好的辦法呢?
根據字符判斷其是否存在于一個字符串中(字符集合), 我們可以想到map,不過其效率也不是很好??紤]到字符都可以用ASCII碼來表示,那么我們就可以用一個數組來表示當X為不同字符時對應的位移長度
/*** 計算位移數組*/public static int[] calShiftArray(String p, int pLen) {int maxNum = 128;//使用ASCII碼總個數作為位移數組的長度int[] shift = new int[maxNum];for (int i = 0; i < maxNum ; i++) {shift[i] = pLen + 1;}//對于模式串中存在的字符,其位移長度=模式串長度-其在模式串中出現的最右位置下標//因為shift計算的時候是i是從小到大,如果遇到兩個相同的字符,那么較右側的位移長度會覆蓋前面的for (int i = 0; i < pLen; i++) {shift[p.charAt(i)] = pLen - i;}return shift;} /*** sunday算法匹配* */public static int sundayAlogrithm( String s, String p) {int sLen = s.length();int pLen = p.length();int[] shift = calShiftArray(p, pLen);int i = 0;//表示與模式串第一位匹配的主串字符下標int j = 0;//表示模式串正在匹配的下標while (i <= sLen - pLen) {j = 0;while (Objects.equals(s.charAt(i+j), p.charAt(j))) {j++;if (j == pLen) {return i;}}//如果遇到P[j]!=S[i+j],說明發生失配,那么就移動模式串,移動長度=shift[x],x為關注的字符下標i = i + shift[s.charAt(i + pLen)];}return -1;}Sunday算法的時間復雜度
從上面的分析來看,Sunday算法大大加速了失配時的位移速度,但是在最壞的情形下,其時間復雜度會變為O(m*n)
假設主串是aaabaaabaaabaaab,模式串是aaaa,我們可以知道aaaa不是上述主串的子串;在使用Sunday算法進行匹配的時候, 大部分情形下,關注字符都是在模式串中存在的(模式串只有a一個字符), 也就是說在大部分情形下模式串都只能移動1位,效率一下子降低了
那么最優的情形的,就是每次失配都可以移動 模式串長度+1 位,那么其時間復雜度就是O(n/m)
簡單來說,Sunday最差情況的時間復雜度為O(m*n), 最優情況的時間復雜度為O(n/m)
參考:
從頭到尾徹底理解KMP(2014年8月22日版)擴展2:Sunday算法
總結
以上是生活随笔為你收集整理的字符串匹配--Sunday算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 终于完全弄懂了KMP(个人理解篇)
- 下一篇: Idea单测执行报错“Command l