problem
- 给定字符串A,B。求串A中可以分割出多少个互不相同的串B(不能重叠)。
solution
模板题,没啥好说的。
KMP匹配:如果成功,就把j==0,从头开始匹配,答案累加。
codes
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1010;
char a[maxn], b[maxn];
int n, m, Next[maxn], f[maxn];
void pre(){
Next[1] = 0;
int j = 0;
for(int i = 1; i < m; i++){
while(j>0 && b[i+1]!=b[j+1])j = Next[j];
if(b[i+1]==b[j+1])j++;
Next[i+1] = j;
}
}
int kmp(){
int ans = 0, j = 0;
for(int i = 0; i < n; i++){
while(j>0 && a[i+1]!=b[j+1])j = Next[j];
if(a[i+1]==b[j+1])j++;
f[i+1] = j;
if(j==m)ans++, j = 0;
}
return ans;
}
int main(){
while(cin>>a+1){
if(a[1]=='#')break;
scanf("%s",b+1);
n = strlen(a+1);
m = strlen(b+1);
pre();
printf("%d\n",kmp());
}
return 0;
}
KMP
介绍:
字符串匹配问题:给定主串A和模式串B,求所有B在A中出现的位置。
传统做法:枚举A串从什么位置开始与B串匹配,然后验证是否匹配。最坏情况A=”aaaaaaaaaaaaaaab”,B=”aaaaaab”,复杂度O(mn)。
KMP:最坏情况复杂度O(n)
具体:
本质原理:
考虑情况(当前i==5):
i = 1 2 3 4 5 6 7 8 9
A = a b a b a b a a b a b
B = a b a b a c b
j = 1 2 3 4 5 6 7 8 9
对于下一个,不再匹配了。传统做法就是把B右移一位继续重头比较。
但是作为一个人,我一眼就看出了右移一位也不行,,,,于是,我们移到他下一个匹配的位置(最长的相同前缀和后缀)在开始比较。并且最好是这个匹配的长度尽可能长。
i = 1 2 3 4 5 6 7 8 9
A = a b a b a b a a b a b
B = a b a b a c b
j = 1 2 3 4 5 6 7 8 9
再考虑到,B移动后的位置,相对A而言的位置关系,其实是和B移动前的位置关系是一样的(因为之前的时候AB匹配上了,所以是字母是完全一样的)。所以就变成了关于B的自我匹配,具体见下方。
两个步骤:
1、对模式串B进行自我匹配:求一个数组next,其中next[i]表示”B中以i结尾的非前缀子串”与“B的前缀”能匹配的最大长度。
next[i]=max{j},j<i且A[i−j+1,i]=A[1,j]
n
e
x
t
[
i
]
=
m
a
x
{
j
}
,
j
<
i
且
A
[
i
−
j
+
1
,
i
]
=
A
[
1
,
j
]
。
2、对字符串A与B进行匹配:求一个数组f,其中f[i]表示”A中以i结尾的子串”与“B的前缀”能够匹配的最长长度。(当长度为B.size()时即得到答案)
具体求法:
1、求next:
咕咕咕