天天看點

BF、KMP算法BF:KMP:next數組:如何計算next數組:寫在最後 以上均為個人學習KMP算法時的想法啊,如有錯誤,請指正!寫在最最後面

字元串比對算法,設定S為文本串,P為模式串。查找P是否是S的子串,即字元串比對

BF:

即暴力周遊:

i,j分别是S、P的下标,一旦不比對,j從0、i從i-j+1開始,[(i-j+1)相當于i回退到和j對應的i的下一位] 該算法複雜度在m*n上,

int BF(char *s,char *p)//查找p是否是s的子串,即 s是主串,p是模式串,
{
    int i=0,j=0;
    while(i<strlen(s)){
        if(*(p+j)=='\0')
            return i-j;
        if(*(s+i)==*(p+j)){
            ++i;
            ++j;
        }
        else{
            //i++; "aaaec","aaec" Wrong
            i=i-j+1;
            j=0;
        }
    }
    return -1;
}
           

KMP:

getNext()是用來獲得Next數組的函數,暫且不管如何獲得,

KMP的想法就是:

諸如:

s:	a b a b a b a c
p:	a b a c
           

KMP利用已經比對上的部分串的資訊,不再像BF算法那樣,通通丢掉。是以說,KMP算法的效率提升就在這裡。

示例講解:

1.現在已經比對上了前3個字元,KMP的操作就是,将p字元串向右移動2位(這是根據相同的字首和字尾的最大長度 來确定的,如何計算出來下文提到)即,i不變,j相應改變–形成p字元串右移2位,成為:

s:	a b a b a b a c
p:	    a b a c
           

這樣就比BF算法,節省了很多重複比對的時間

(比如說,如果按照BF,比對失敗之後,下一步應該是:

s:	a b a b a b a c
p:	  a b a c 
           

int KMP(char *s,char *p)
{
    int *next=(int *)malloc(sizeof(int)*strlen(p));
    getNext(p,next);
    //s是主串,p是模式串,通過分析已比對的字元串的字首和字尾的最大子集,比對失敗的時候來跳過某些已經完成比對的字元
    int i=0,j=0;
    while(i<strlen(s)){
        if(*(p+j)=='\0')
            return i-j;
        if(*(p+j)==*(s+i)){
            ++i;
            ++j;
        }
        else{
            if(j==0)
                i++;
            else {
                j=next[j-1];//相比BF,最主要的改變
            }
        }
    }
    return -1;
}
           

next數組:

KMP主要的思想就是:

假設現在s[i]!=p[j],而在此之前,s[i]和p[j]是相等的,也就是一個字元串,

s: a b a b a b a c
p: a b a c
           

如上面的例子,在i=j=3之前,s、p是“aba”,是同一個字元串,此時我們讨論之前相等的這部分字元串的字首和字尾:

字首(不包含最後一個字母):		字尾(不包含第一個字母):
a							a
ab						       ba
           

注意:ab和ba(順序要注意) ,不是一個相同的串

是以,”aba"的字首字尾公共最大長度位1,這個的意思是

a b a
    a b a
           

将一個字元串平移之後,它的字首最多能與字尾重複的元素的個數!這樣,kmp就可以省下來 字首和字尾一樣的元素的比對時間。

如何計算next數組:

1.可以按照next數組的定義,即next[i]表示前i個元素組成的字元串的字首字尾最大公共長度

2.快速求解next數組,本身就是在字元串比對:

求aabaa的字首字尾最大公共長度

首先,第一個子串,“a”沒有字首和字尾,我在這裡把它設定位0(因為有些參考設定為-1)

後續的next數組的值,即 将字元串與自身比對

從下面這個情況開始(上面的串i=1,下面的串j=0):

a a b a a
  a a b a a
           

如果p[i]和p[j]相等,next[i]的值就能确定了,注意是i;next[i]=next[i-1]+1;

這主要是 前i位的串的字首字尾公共長度與前i-1位的串的情況有關

如果不相等,情況多一些 :

(這裡求next數組時的這種情況可能有點問題,過幾天修改)

如果是下面這兩種,next[i]=0,然後i++,j=0

i					    i	
a b ....				a a b a a
  a b......				  a a b a a
           

但這種解決辦法并不能涵蓋所有情況,如下面這種:

i
a b a b a a
    a b a b a a 	
          j
           

這種情況就說明,上面那種解決辦法,會造成:

盡管p[i]和p[j]不比對,但不能立刻把next[i]定位0

還需要考慮p[i]是否和p[0]相比對

若比對----則應該next[i]=1

不比對應該next[i]=0

下面是next數組的求解方法:

void getNext(char *p,int *next)
{
    int i=1,j=0;//p="aaec"
    next[0]=0;
    while(i<strlen(p)){
        if(*(p+i)==*(p+j)){
            next[i]=next[i-1]+1;
            i++;
            j++;
        }
        else{
            j=0;
            if(*(p+i)!=*(p+j)){//發現不相等的i、j之後,第一步要做的是立刻把j=0,然後在比較一次
                next[i]=0;
                j=0;
                i++;
            }
            else{
                next[i]=1;
                i++;
                j++;
            }
        }
    }
    //next[0]=-1;
}
           

寫在最後 以上均為個人學習KMP算法時的想法啊,如有錯誤,請指正!

寫在最最後面

寫完kmp也能發現,kmp提高效率是對模式串p有一定的要求的,在處理不符合要求的模式串的時候,時間還不如BF,是以下一步可以仔細讨論一下不同情況下,KMP對模式串比對的效率影響。

繼續閱讀