模板
如何在目标串中找到模式串p?
1、暴力枚举起始位置,逐位比较
缺点:枚举太多无用位置,时间复杂度高
Codes:
1 int j;
2 for(int i = 0;s[i];++ i){
3 for(j = 0;p[j];++ j)
4 if(s[i + j] != p[j])
5 break;
6 if(!p[j]){ //匹配成功
7 //Operations
8 }
9 }
复杂度:O((n - m + 1) * m) (n:原串长度,m:匹配串长度)
2、KMP
优点:只枚举了可能有用的位置(其实就是暴力缺点opp. QwQ)
那是如何做到只枚举有用位置的呢?
nxt数组!
①nxt[i + 1] 定义为最大的 j + 1 使得 p[0~j] 是 p[0~i] 的后缀
②nxt[i] 定义为当 i 位置不匹配时,将跳到 nxt[i] 位置重新匹配(关于nxt数组定义众说纷纭,这里只给出我认为最易懂的定义QwQ)
因为再不匹配时kmp算法并不是再枚举下一位,而是跳到 nxt[i] 继续匹配,所以会明显提高效率,时间复杂度降到线性(原串不回溯)
例子1:
P : 0 1 2 3 4 5 6 7
a b c a b c a d
nxt: -1 0 0 0 1 2 3 4
例子2:
P : 0 1 2 3 4 5 6 7
a b a c b a b a
nxt: -1 0 0 1 0 0 1 2
例子3 :
P : 0 1 2 3 4 5 6 7
a a a a a a a a
nxt: -1 0 1 2 3 4 5 6
清楚定义之后,计算nxt[]的思路也差不多了
Codes:
1 void get_nxt(char *p){
2 int i = 0,j = -1;
3 nxt[0] = -1; //边界
4 while(p[i]){
5 if(j == -1 || p[j] == p[i]) //到达边界 或 前缀等于后缀
6 nxt[++ i] = ++ j; //可以跳到的位置 + 1
7 else j = nxt[j]; //不匹配则跳到 nxt[]
8 }
9 return;
10 }
接下来是匹配,可以观察其与暴力的不同或相同点
Codes:
1 int kmp(char *p,char *s){ //传指针,可以理解为数组
2 //s:目标串,p:模式串
3 int res = 0; //计数,出现次数
4 get_nxt(p); //处理nxt
5 for(int i = 0,j = 0;s[i];++ i){
6 while(j != -1 && s[i] != p[j])
7 j = nxt[j]; //若不匹配,跳到 nxt[j]
8 ++ j; //匹配上
9 if(!p[j]){ //模式串与目标串匹配完成
10 res ++; //计数 ++
11 j = nxt[j]; //若计算重叠串则返回nxt[j]
12 //Operations
13 }
14 }
15 return res;
16 }
模板(其实就是把上面搬过来2333)
Codes:
1 #include<iostream>
2 #include<algorithm>
3 #include<cstring>
4 #include<cstdio>
5 #define N 1010
6
7 using namespace std;
8
9 char s[N],p[N];
10 int nxt[N];
11 void get_nxt(char *p){
12 int i = 0,j = -1;
13 nxt[0] = -1; //边界
14 while(p[i]){
15 if(j == -1 || p[j] == p[i]) //到达边界 或 前缀等于后缀
16 nxt[++ i] = ++ j; //可以跳到的位置 + 1
17 else j = nxt[j]; //不匹配则跳到 nxt[]
18 }
19 return;
20 }
21 int kmp(char *p,char *s){ //传指针,可以理解为数组
22 //s:目标串,p:模式串
23 int res = 0; //计数,出现次数
24 get_nxt(p); //处理nxt
25 for(int i = 0,j = 0;s[i];++ i){
26 while(j != -1 && s[i] != p[j])
27 j = nxt[j]; //若不匹配,跳到 nxt[j]
28 ++ j; //匹配上
29 if(!p[j]){ //模式串与目标串匹配完成
30 res ++; //计数 ++
31 j = nxt[j]; //若计算重叠串则返回nxt[j]
32 //Operations
33 }
34 }
35 return res;
36 }
37 int main(){
38 scanf("%s%s",s,p);
39 cout << kmp(p,s) << '
';
40 return 0;
41 }
复杂度:O(n + m)(n:原串长度,m:匹配串长度)