標籤:訪問 檔案 http 利用 ring 轉移 abc 首碼 怎麼辦
例題傳送門:caioj1177
KMP模版:子串是否出現
【題意】
有兩個字串SA和SB,SA是母串,SB是子串,問子串SB是否在母串SA中出現過。
如果出現過輸出第一次出現的起始位置和結束位置,否則輸出"NO"
【輸入檔案】
第一行SA(1<= 長度<=1000000)
第二行SB(1<= 長度<=1000)
【輸出檔案】
如果SB在SA中出現過輸出第一次出現的起始位置和結束位置,否則輸出"NO"
【範例1輸入】
aaaaabaa
aab
【範例1輸出】
4 6
【範例2輸入】
aaaaabaa
aax
【範例2輸出】
NO
演算法分析:
KMP其實就是一種偷懶的方式,通過訪問前面已經遍曆過的位置來首先給定一個可繼承的值,然後暴力(汗......)
首先定義一個p數組,p[i]=j時,代表SA字串[1...j]與SA字串[i-j+1...i]所形成的子串完全相同
那麼我們怎麼得出這個p數組呢?
就是暴力+偷懶!!!(其實前面已經講了,尷尬)
當我們訪問到i這個點時,我們先訪問一下i-1,因為我們訪問到i的時候已經把1~i-1的p數組求出來了,那麼請看下面的圖:
我們設j=p[i-1],i-1-j+1=i-j,那麼我們可以知道SA[1...j]=SA[i-j...i-1],也就是的紅色區間,那假如SA[j+1]=SA[i]的話,我們就可以直接p[i]=j+1對吧
但是現實是殘酷的(AC沒有那麼容易~),那麼當SA[j+1]!=SA[i]的時候怎麼辦呢?那麼我們就找p[j]!!!
我們再設一個變數k=p[j],因為p[j]的定義,所以我們可以得到SA[1...k]=SA[j-k+1...j],也就是前兩個棕色區間,這時因為兩個紅色區間相同,所以我們可以得到SA[1...k]=SA[i-j...i-j+k-1],也就是第一個和第三個棕色區
間相等,接著又可以得到SA[j-k+1...j]=SA[i-k(i-1-k+1)...i-1],也就是第二個和第四個的棕色區間相等,然後!!!我們得到四個棕色區間相等!!!我們就可以判斷SA[k+1]和SA[i]是否相等來求p[i],如果相等,p[i]=k+1,
否則繼續找p[k]。一直找啊找,直到找到沒得找也就是當前的p[p[p[p[p[p[.......的值為0時,那麼我們就判斷SA[1]和SA[i]是否相同,相同p[i]=1,否則p[i]=0
其實也不算暴力,而是利用p數組的定義來尋找前面可以用來繼承的點!!
但是到了這裡,還沒有完成KMP的學習
我們的p數組只對SA字串,也就是題目中的子串進行了處理
那麼p數組對於我們求答案有什麼協助呢?
我們設j為SB字串中的i-1位置開始所組成的尾碼,與SA字串中的1位置開始的首碼的最長公用長度,也就是SA[1...j]=SB[i-j(i-1-j+1)...j-1],那麼我們可以判斷SA[j+1]和SB[i]是否相等,相等就記錄j的值,不相等就找p[j]!!!是不是似曾相識的步驟,沒錯!就是把SB中的i轉移到SA中進行求解,這樣就是KMP的全部了!
參考代碼:
#include<cstdio>#include<cstring>using namespace std;char sa[1110000],sb[1100];//sa是母串,sb是子串int p[1100];//p數組是為子串準備的,p的含義和母串一點關係都沒有。//p[i]表示sb中 以第i個字元為結尾,往前最多拉多少個字元(sb[i]結尾的尾碼)可以完全符合sb的首碼//比如 sb="abcdabc"中 p[7]=3, 就這樣,我當你理解p[i]的含義了int main(){ int lena,lenb,i,j; scanf("%s",sa+1);lena=strlen(sa+1); scanf("%s",sb+1);lenb=strlen(sb+1); //製造P數組 p[1]=0; for(i=2;i<=lenb;i++) { j=p[i-1];//先記錄p[i-1] while(j>0&&sb[i]!=sb[j+1]) j=p[j]; /* 這裡有兩種情況 如果j+1的位置的字元等於i位置的字元的話,那就直接把p[i]=j+1 否則就找j的p值,因為1~sb[p[j]]一定是1~sb[j]的尾碼 */ if(sb[i]==sb[j+1]) p[i]=j+1;else p[i]=0; } int st,ed; j=0; for(i=1;i<=lena;i++)//緊接著用p數組來匹配母串 { while(j>0&&sa[i]!=sb[j+1]) j=p[j]; if(sa[i]==sb[j+1]) j++; if(j==lenb){ed=i;st=i-lenb+1;break;}//得到答案後就記錄開頭與結尾 } if(j==lenb) printf("%d %d\n",st,ed); else printf("NO\n"); return 0;}
演算法導論————KMP