一文搞懂BF算法與KMP算法

一文搞懂BF算法與KMP算法

一.暴力算法(BF算法)

在這裏插入圖片描述
假設我們現在要對上面的字符串進行匹配(假設上面那個串是P,下面那個串是T串)。最開始我們看T的前三個串和P是匹配的,第四個發現不匹配於是我們右移一下
在這裏插入圖片描述
發現不匹配,再右移一步
在這裏插入圖片描述
一直這樣直到
在這裏插入圖片描述
於是停止,當然也有可能最後沒有對應的匹配串。
代碼實現:

import org.junit.Test;
public class Main {
    //從P裏面找T的匹配串並返回對應索引的位置,如果沒有返回-1
    int BF(String P,String T){
        int i=0,j=0,current=0;
        while(j<T.length()&&current<P.length()){
            while(current<P.length()&&j<T.length()&&T.charAt(j)==P.charAt(current)){
                current++;
                j++;
            }
            if(j==T.length())
                return i;
            else
            {
                j=0;
                i++;
                current=i;
            }
        }
        if(j==T.length())
            return i;
        else
            return -1;
    }
    @Test
    public void Test(){
        System.out.println(BF("abaacababcac","ababc"));
    }
}

在這裏插入圖片描述
算法複雜度O((m-n)*n)=O(m*n-n^2)

二.KMP算法

在看KMP算法前我們需要知道爲什麼需要KMP算法?BF算法他不香嗎?
我們先看個例子:
在這裏插入圖片描述
像這樣的就會無辜的浪費很多事間去比較。於是KMP就誕生了。

1.prefix table(前綴表)

在這裏插入圖片描述
我們還是來匹配這兩個串。
我們看下面的T串"ababc"
他的前綴串爲(我們不看他本身)

編號 前綴串
1 a
2 ab
3 aba
4 abab

現在我們需要找到每個前綴串他的前綴與後綴(不考慮串本身)相等的最大長度
於是

前綴串 長度
a 0
ab 0
aba 1
abab 2

接下來就是我們的前綴表:

索引 T串 前後綴相等最大長度
0 a -1
1 b 0
2 a 0
3 b 1
4 c 2

上面這個前綴表我們可以看成一個數組,取名爲Pre;大小爲5
那麼pre[i]的含義是:

0~(i-1)這i個字符的前後綴相等最大長度,特別的當i=0時我們賦值爲-1來區分

2.前綴表的作用

還是前面的P串與T串
在這裏插入圖片描述
最開始從P[0]與T[0]對齊,開始匹配發現P[3]與T[3]不匹配,於是我們看Pre[3]=1,那麼就把T[1]與P[3]對齊再來比較:
在這裏插入圖片描述
從新的對齊位置P[3]與T[1]開始往後比較(也即綠線處那裏),不匹配,我們看Pre[1]=0,於是我們把T[0]與P[3]對齊
在這裏插入圖片描述
這時從上面的綠線處開始比較,往後比發現T[1]與P[4]不匹配了,這時Pre[1]=0,於是P[4]與T[0]對齊
在這裏插入圖片描述
這時從上面的綠線處開始比較,發現T[0]與P[4]不匹配了,這時Pre[0]=-1,於是P[4]與T[-1]對齊,而實際上T[-1]使我們虛構出來的,實際上是T[0]與P[5]對齊了
在這裏插入圖片描述
這個時候已經匹配上了,當然在這裏還沒結束,我們確實找到了第一個,但是後面可可能還有,因此下一步我們應該看T匹配完成的最後一個字符即T[4],這裏Pre[4]=2,於是我們T[2]與P[9]對齊

這時繼續前面的操作,最後到這裏纔是真正的結束了,現在我們對KMP算法已經瞭解了

3.代碼實現

實際上這裏的Pre數組就是我們常說的next數組

import org.junit.Test;
/**
 * @author  jackTan
 */
public class MainTest {
    /**
     * 在串P中匹配T串,只匹配第一個,如果沒有就返回-1,否則返回匹配到的P中的索引
     * @param p
     * @param t
     * @return
     */
    int Kmp_Search(String p,String t){
        //第一步獲取前綴串
        int []pre = getPre(t);
        //i指向p串的字符,j指向t串的字符
        int i=0,j=0;
        while(i<p.length()&&j<t.length()){
            if(j==-1){
                j++;
                i++;
            }
            while(i<p.length()&&j<t.length()&&p.charAt(i)==t.charAt(j)){
                i++;
                j++;
            }
            if(j==t.length()){
                break;
            }
            else{
                j=pre[j];
            }
        }
        if(j==t.length()){
            return i-t.length();
        }else{
            return  -1;
        }

    }

    /**
     * 該方法獲取字符串的前綴表
     * @param T
     * @return
     */
    int[] getPre(String T){
        int []pre= new int[T.length()];
        pre[0] = -1;
        for(int i=1;i<T.length();i++){
            pre[i]=getPreOne(T,i);
        }
        return pre;
    }

    /**
     * @param T 目標串
     * @param endIndex 表示要獲取0-endIndex的前綴值
     * @return
     */
    int getPreOne(String T,int endIndex){
        int length = T.length();
        int i=0;
        for(;i<length-1;i++){
            if(T.charAt(i)!=T.charAt(length-1)){
                break;
            }
        }
        return i;
    }

    @Test
    public void Test(){
        System.out.println(Kmp_Search("abaacababcac", "ababc"));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章