关于模式匹配
可以用暴力,直接遍历
while(i<a.length()&&j<b.length() ){
if(a[i]==b[j]){
i++;
j++;
}
else{
i=i-j+2;//i-(j-1)+1,i减去j右挪的真正位数,就是之前i之前所在的位置,+1就是往后挪一位
j=0;
}
}
if(j==b.length() ) {
cout<<i-j;
}
但是容易RE,所以我们要学习KMP,
i 不往回挪,不匹配时 j 直接跳next数组,那么next数组是怎么求的呢?网上有很多很多的详解,然而我都看不懂,在某天我顿悟了,下面是我理解的next[]是怎么来的
先求子串的前缀,再求子串的前缀的相同的前后缀的长度,next[k]的k就是子串的长度
所以abaabbabaab的next数组为0 0 1 1 2 0 1 2 3 4 5
然后我手动尝试了其他的字符串的next数组,发现是可以这样子理解的,比如下面这个
例子就是这样求的
那么接下来看看代码
#include<cstring>
#include<iostream>
using namespace std;
char a[100005],b[100005];
int next[100005];
int m,n;
void getnext(){
int i=0,j=-1;
next[0]=-1;
while(i<m){
if(j==-1||b[i]==b[j]){//手动模拟看下图
i++;
j++;
next[i]=j;
}
else j=next[j];
}
}
int kmp(){
int i=0,j=0;
getnext();
while(i<n){
if(j==-1||a[i]==b[j]){//模式匹配
i++;
j++;
}
else j=next[j];//不匹配就跳next数组
if(j==m) return i-j;
}
return -1;
}
int main(){
cin>>a;
cin>>b;
n=strlen(a);
m=strlen(b);
cout<<kmp();
return 0;
}
拿上面表中子串为例子,手动模拟
kmp简单题集1(http://acm.hdu.edu.cn/showproblem.php?pid=1711)
2 http://acm.hdu.edu.cn/showproblem.php?pid=3746
第二题Cyclic Nacklace
kmp求循环节,利用了getnext求循环节,
Output
For each case, you are required to output the minimum count of pearls added to make a CharmBracelet.
Sample Input
3 aaa abca abcde
Sample Output
0 2 5
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=100010;
char str[MAXN];
int next[MAXN];
void getNext(char *p)
{
int j,k;
j=0;
k=-1;
int len=strlen(p);
next[0]=-1;
while(j<len)
{
if(k==-1||p[j]==p[k])
{
j++;
k++;
next[j]=k;
}
else k=next[k];
}
}//求next数组
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",&str);
getNext(str);
int len=strlen(str);
if(next[len]==0)//如果最后一个字符串的next为0,就是说没有相同的前后缀,则循环节为0
{
printf("%d\n",len);
continue;
}
int t=len-next[len]; //否则减去循环的长度
if(len%t==0)printf("0\n");//如果循环的长度是len的倍数,则有循环节,则不用添加任何字符即为循环
else
{
printf("%d\n",t-len%t);//否则算出需要多少个字符才能循环
}
}
return 0;
}
—如有不对,请指教