KMP

應用範圍

對於一個匹配串(S)和單個或多個母串(B)的問題

流程

  • 先對匹配串造fail數組和AC自動機中的fail類似,即以失敗的節點爲右端點的

這裏寫圖片描述

  • 複雜度約爲On+m
void pre(){
    fail[1]=0;fail[2]=1;
    for(int i=1;i<=m;i++){
        int j=fail[i];
        while(j&&S[i]!=S[j])j=fali[j];
        //此時滿足S[i-j+1,i]和S[1,j]相等 
        fail[i+1]=j+1;
    }
}
  • 和母串進行匹配
void match(){
    for(int i=1,j=1;i<=n;i++){
        while(j&&B[i]!=S[j])j=fali[j];
        //此時滿足B[i-j+1,i]和S[1,j]相等 
        j++;
        if(j=m+1){
            //get the ans
            j=fail[j];
        }
    }
} 

一些變形

  • 對於一些問題相等的判斷肯定不會只是數值的相等,可能有一些奇葩的定義

例1

  • 我們定義兩個字符串相等:是他們的其中的每個字符在該段中的相對大小一一對應,就像下面的例子(串中字符大小屬於[1,26])
5 6 2 10 10 7 3 2 9
    1  4  4 3 2 1
  • 此時我們只需改變判斷相等的方法即可,但是一定不能忽略了匹配的流程:一個一個的判斷(即當前有一對相同的串,我們只需判斷它們的尾端加入後新的串是否相等)
  • 我們發現字符大小很小,於是我們可以記錄一個關於[1,26]前綴數組即可
bool cmp(int A[M][30],int l1,int r1,int a,int B[M][30],int l2,int r2,int b){
    for(int i=1;i<=t;i++){
        ta[i]=A[r1][i]-A[l1-1][i]+ta[i-1];
        tb[i]=B[r2][i]-B[l2-1][i]+tb[i-1];
    }
    return ta[a-1]==tb[b-1]&&ta[a]==tb[b];//加入前後都相等 
}
int main(){
    /**讀入,構造前綴和**/ 
    fail[2]=1;
    for(int i=2,j;i<=m;i++){
        j=fail[i];
        while(j&&!cmp(Ss,1,j-1,S[j],Ss,i-j+1,i-1,S[i]))j=fail[j];
        fail[i+1]=j+1;
    }
    for(int i=1,j=1;i<=n;i++){
        while(j&&!cmp(Bs,i-j+1,i-1,B[i],Ss,1,j-1,S[j]))j=fail[j];
        j++;
        if(j==m+1)ans[++ans[0]]=i-m+1,j=fail[j];
    }
}

例2

  • 對於兩端串,如果它們的“樣子一樣”,我們認爲它們相等
a   b   c   x  c    z   z   a   b   c 
      prvi dr prvi  tr  tr  x   

-如圖prvi>c||dr>x||tr>z||x>a (唯一對應)
- 方法很簡單,我們只需記錄每個位置的字符上一次出現的下標,即可判斷一個新的點(段尾後一個)在該段中的位置,用它判斷即可

bool cmp1(int l1,int r1,int l2,int r2){
    int ra=nxts[r1];
    int rb=nxts[r2];
    if(ra<l1&&rb<l2)return 1;
    if(ra==rb)return 1;
    return 0;
}
bool cmp2(int l1,int r1,int l2,int r2){
    int ra=nxts[r1];
    int rb=nxtb[r2];
    if(ra<l1&&rb<l2)return 1;
    if(ra-l1==rb-l2)return 1;
    return 0;
}
int main(){
    /**讀入&映射&造nxt**/ 
    fail[2]=1;
    for(int i=2,j;i<=m;i++){
        j=fail[i];
        while(j&&!cmp1(1,j,i-j+1,i))j=fail[j];
        fail[i+1]=j+1;
    }
    for(int i=1,j=1;i<=n;i++){
        while(j&&!cmp2(1,j,i-j+1,i))j=fail[j];
        j++;
        if(j==m+1){
            printf("%d",i-m+1);
            return 0;
        }
    }
}
發佈了95 篇原創文章 · 獲贊 183 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章