kmp字符串匹配

比较推荐的一篇blog


(感觉这个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();}

 


 

比较好的一道题: [BOI2009]

推荐blog

把字符串分成好几段

其中每段大小相等 , 且最大的真后缀与真前缀的真后缀的补集完全占且只占一段 , 成为 p 段

这样能证明

 

 p与a段相等 a与b段相等 b与c段相等

显然 , p段就是答案

所以 ans = len - next[n]

posted @ 2019-10-27 14:50  ChiaroShiro  阅读(177)  评论(0编辑  收藏  举报