KMP 算法是 D.E.Knuth、J,H,Morris 和 V.R.Pratt
三位神人共同提出的,称之为 Knuth-Morria-Pratt 算法,简称 KMP 算法。
问题:求 a 字符串与 b 字符串中子串相同的串首位置。
暴力就不说了,设 a 长 \(m\),b 长
\(n\),每次枚举比对每个字符,复杂度
\(O(nm)\)。
KMP
主要思想:如果一个字符串的子串与前缀相等,那么在查找时就可以直接将前缀跳至该子串的位置。复杂度
\(O(n)\)。
nxt[x]
记录x位置字符在查询串中的跳转位置。
eg:对于串abaabacac:
a b a a b a c a c
-1 0 0 1 1 2 3 0 1
1 2 3 4 5 6 7 8 9 10
| inline void getnxt() { int j=0,k=-1; nxt[0]=-1; while(j<s.length()-1) { if(k==-1||s[j]==s[k])nxt[++j]=++k; else k=nxt[k]; } }
|
查询时跳转即可。
模拟一下就知道啦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> using namespace std; string s,t; int nxt[1000]; inline void getnxt() { int j=0,k=-1; nxt[0]=-1; while(j<s.length()-1) { if(k==-1||s[j]==s[k])nxt[++j]=++k; else k=nxt[k]; } } inline int indexkmp() { int i=0,j=0; while(i<s.length() and j<t.length()) if(i==-1 or s[i]==t[j])i++,j++; else i=nxt[i]; if(i>=s.length())return j-s.length(); return -1; } int main() { std::ios::sync_with_stdio(false); cin>>s>>t; getnxt(); cout<<indexkmp()+1; return 0; }
|
例题
洛谷P3375
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<iostream> #include<cstdio> #include<string> #include<cstring> using namespace std; const int maxn=1e6+5; int nxt[maxn]; char a[maxn],b[maxn]; int lena,lenb; inline void getnxt() { int k=-1,j=0; nxt[0]=-1; while(j<lena) if(k==-1 or a[k]==a[j]) nxt[++j]=++k; else k=nxt[k]; } inline void indexkmp() { int j=0,i=0; while(j<lena and i<lenb) if(j==-1 or a[j]==b[i]){ i++,j++; if(j==lena){ printf("%d\n",i-lena+1); j=nxt[j]; } } else j=nxt[j]; } int main() { scanf("%s\n%s",&b,&a); lena=strlen(a); lenb=strlen(b); getnxt(); indexkmp(); for(int i=1;i<=lena;i++) printf("%d ",nxt[i]); return 0; }
|