6、旋轉陣列的最小數字
作用:演算法的核心是利用匹配失敗後的資訊,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是通過一個next()函式/ 失配陣列 實現,函式本身包含了模式串的區域性匹配資訊。
時間複雜度:O(m+n)
核心:失配陣列
1)最長公共前後綴作用:
eg: 文字串s abeabeabf
模式串s1 abeabf
如上兩串匹配到畫線部分時會失配
普通匹配會倒退主串到s[2],即b位置,然後重新匹配串s1,如果不倒退直接往前呢?那是不行的(考慮該情況 s: aaaab s1: aaab)
而觀察到兩串已匹配且未失配的部分,其最長公共前後綴為ab,即文字串能重新與模式串進行匹配、文字串涵蓋有模式串字首的地方即字尾所在處,只有這一處是兩串有可能匹配成功的且長度最長的,所以可以直接將模式串的字首ab移動到字尾ab處,文字串不動,繼續匹配
2)含義:next[i]表示串s 0~i-1部分的公共最長前後綴,進行一點優化(當s[j]==s[k],將 next[j]=k 變為 next[j]=next[k])
3)作用:記錄好匹配過的部分的狀態,使失配時主串不需要回退,只需利用nex記錄移動更新nex陣列的位置,避免重複匹配,達到快速匹配。
4)優化
優化前:
1 while(j<len){ 2 if(k==-1||s[j]==s[k]) 3 ++j,++k,nex[j]=k; //nex[i]等於i前最長公共前後綴,記錄好匹配過的狀態 4 else k=nex[k];5 }
優化後:
eg: 文字串s aabcabe
模式串s1 abcabd 字串匹配到s1串的d和s串的e處失配,為繼續下一步匹配,呼叫d的nex,即nex[6]=3,因為d前面部分與模式串相同的字元截止到c,
模式串s2 abcabc 字串匹配到s2串的d和s串的e處失配,為繼續下一步匹配,呼叫c的nex,此時nex[6]=0,即失配此時並未失配,若nex[6]=3,c失配時會 返回到字首對應相等的c,但是該公共綴“abc”在之前兩串匹配時已經顯示匹配失敗,所以現在會出現重複匹配的情況,此時應該為 nex[6]=nex[nex[6]];
規律(理解最重要):
當 tj = tnex [ j ] , nex [ j ] = nex [ nex [ j ] ];
else nex [ j ] = nex [ j ];
1 while(j<len){ 2 if(k==-1||s[j]==s[k]){ 3 ++j,++k; 4 if(s[j]!=s[k])nex[j]=k; 5 else nex[j]=nex[k]; 6 //未失配情況,k、j代表的就是字首與字尾相等部分的末尾,兩者完全相同,所以直接再nex一次, 7 } 8 else k=nex[k]; 9 }
模板:
1 //建失配陣列 2 void getnext(char *p,int nex[]){ 3 int len=strlen(p),k=-1,j=0; 4 nex[0]=-1; 5 while(j<len){ 6 if(k==-1||p[j]==p[k]){ 7 ++j,++k; 8 if(p[j]!=p[k])nex[j]=k; 9 else nex[j]=nex[k]; 10 } 11 else k=nex[k]; 12 } 13 } 14 //模式串匹配文字 15 int kmp(char *s,char *p){ 16 int i=0,j=0; 17 int len1=strlen(s),len2=strlen(p); 18 while(i<len1){ 19 if(j==-1||s[i]==p[j]) 20 i++,j++; 21 else 22 j=nex[j]; 23 if(j==len2) 24 j=-1,i--,mark++; 25 } 26 return mark; 27 }View Code