kmp字符串匹配
(感觉这个blog就是写给自己看的 ... 不太建议想学kmp的来看这篇)
kmp字符串匹配
abababf
ababf
这两个串匹配 , 第五个字符的时候失配
暴力再匹配:
abababf
ababf
kmp再匹配
abababf
ababf
很显然 , 一个串的真后缀在失配的时候可以跳到与之完全相等的一个真前缀的位置上(见上例)(这里是除去当前字符也就是最前面这个字符的真后缀和臻前缀)
那怎么跳呐
需要一个数组next
next指向在这个字符失配时应该跳向的匹配串的下标
显然next[0]=next[1] , 因为要真后缀与真前缀相同 , 0, 1显然没有满足上述条件的前缀和后缀
next存的其实是最大长度的完全相同的真后缀与真前缀
因为跳的位置就是长度
next[i]=j
表示 (0...j) 与 (i .. len) 这两个(分别是前缀和后缀)完全相同
这样在处理的next数组时 p[i]==p[k]时可以依靠 k = next[k] 快速找到符合条件的真前缀真后缀
这样kmp匹配就简单了 , 按照上述做法做就是了
1 //#pragma GCC optimize(2) 2 #include<cmath> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<vector> 7 #include<iostream> 8 #include<algorithm> 9 #define N 510 10 #define debug 1 11 #define osu auto 12 #define FILETEST 1 13 #define inf 1000010 14 #define ll long long 15 #define ha 998244353 16 #define INF 0x7fffffff 17 #define pii std::pair <int, int> 18 #define INF_T 9223372036854775807 19 #define APART puts("----------------------") 20 #define DEBUG printf("%s %d\n",__FUNCTION__,__LINE__) 21 22 namespace chino{ 23 24 inline void setting(){ 25 #if FILETEST 26 freopen("_test.in", "r", stdin); 27 freopen("_test.me.out", "w", stdout); 28 #endif 29 return; 30 } 31 32 inline int read(){ 33 char c = getchar(), up = c; int num = 0; 34 for(; c < '0' || c > '9'; up = c, c = getchar()); 35 for(; c >= '0' && c <= '9'; num = (num << 3) + (num << 1) + (c ^ '0'), c = getchar()); 36 return up == '-' ? -num : num; 37 } 38 39 int sl, pl; 40 int next[inf]; 41 char s[inf], p[inf]; 42 43 inline void getNext(){ 44 int k = 0; 45 next[1] = next[0] = 0; 46 for(int i = 1; i < pl; i++){ 47 while(k && p[i] != p[k]) k = next[k]; 48 next[i + 1] = (p[i] == p[k] ? ++k : 0); 49 } 50 return; 51 } 52 53 inline void kmp(){ 54 int k = 0; 55 for(int i = 0; i < sl; i++){ 56 while(k && s[i] != p[k]) k = next[k]; 57 k += (s[i] == p[k]); 58 if(k == pl) printf("%d\n", i - pl + 2); 59 } 60 return; 61 } 62 63 inline int main(){ 64 scanf("%s%s", s, p); 65 sl = strlen(s); 66 pl = strlen(p); 67 getNext(); 68 kmp(); 69 for(int i = 1; i <= pl; i++) 70 printf("%d ", next[i]); 71 puts(""); 72 return 0; 73 } 74 75 }//namespace chino 76 77 signed main(){return chino::main();}
把字符串分成好几段
其中每段大小相等 , 且最大的真后缀与真前缀的真后缀的补集完全占且只占一段 , 成为 p 段
这样能证明
p与a段相等 a与b段相等 b与c段相等
显然 , p段就是答案
所以 ans = len - next[n]