先上两大神的文章帮助大家理解KMP
http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
https://www.cnblogs.com/yjiyjige/p/3263858.html
https://blog.csdn.net/v_july_v/article/details/7041827
之后就直接上板子了
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<algorithm>
using namespace std;
char s1[100005];
char s2[100005];
int next[100005];
int n,len1,len2;
void getnext() {//基础版next数组
int j=0,k=-1;
next[0]=-1;
while(j<len2){
if(k==-1||s2[j]==s2[k])
{
next[++j]=++k;
}
else
k=next[k];
}
}
void getnext(){//优化版 next数组
int j=0,k=-1;
next[0]=-1;
while (j<len2){
if(j==-1||s2[j]==s2[k])
{
++j;
++k;
if(s2[j]!=s2[k])
next[j]=k;
else
next[j]=next[j];
}
else
k=next[k];
}
}
int kmp_index(){//统计第一次匹配出现的位置
int i=0;//主串的位置
int j=0;//模式串的位置
getnext();
while(i<len1 && j<len2)
{
if(j==-1||s1[i]==s2[j])
{
i++;
j++;
}
else if(j!=-1&&s1[i]!=s2[j])
j=next[j];
}
if(j==len2)
return i-j;
else
return -1;
}
int kmp_count(){//统计匹配字符串出现的个数,可以重叠
int ans=0,i=0,j=0;
while(i<len1)
{
if(j==-1||s1[i]==s2[j])
{
i++;
j++;
}
else
j=next[j];
if(j==len2)
{
ans++;
j=next[j];
}
}
return ans;
}
int main(){
scanf("%d",&n);
while(n--){
scanf("%s",s1);
scanf("%s",s2);
len2=strlen(s2);
len1=strlen(s1);
getnext();
//for(int i=0;i<=len2;i++)
//printf("%d ",next[i]);
int ans_index=kmp_index();
int ans_count=kmp_count();
printf("%d\n",ans_index);
printf("%d\n",ans_count);
}
return 0;
}
HDU 3746 next数组性质
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3746
题目大意:
给你一个字符串,要求将字符串的全部字符最少循环2次需要添加的字符数。
例子:
扫描二维码关注公众号,回复:
4120674 查看本文章
abcabc 已经循环2次,添加数为0
abcac 没有循环2次,添加字符abcac。数目为5.
abcabcab 已经循环过2次,但第三次不完整,需要添加数为1
我目前只能看懂未优化版的kmp每个字符的对应的next数组的值 就是 最大前缀等于最大后缀的长度
上代码,在代码中体会奥义
自己在纸上画一画,体会一下next数组的意义和性质
#include<iostream>
#include<string.h>
using namespace std;
int next[1000005];
char a[100002];
int len;
void getnext()
{
int i=0,j=-1;
next[0]=-1;
while(i<len)
{
if(j==-1 || a[i]==a[j])
next[++i]=++j;
else j=next[j];
}
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s",a);
len=strlen(a);
getnext();
int lent=len-next[len];//循环节的长度
if(lent!=len&&len%lent==0)//如果循环节的长度不等于字符串长度,并且字符串长度%循环节长度为0
{
cout<<"0"<<endl;
}
else
cout<<lent-next[len]%lent<<endl;
}
}
题目连接:http://poj.org/problem?id=2406
这题也运用到了next数组的性质 循环节的长度。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000005;
int next[maxn];
void get(char *s) {
int l = strlen(s);
int j = 0, k = -1;
next[0] = -1;
while(j < l) {
if(k == -1 || s[j] == s[k]) {
next[++j] = ++k;
} else {
k = next[k];
}
}
}
char s[maxn];
int main() {
while(gets(s) ) {
if(strcmp(s,".") == 0) {
break;
}
get(s);
int ans = 1;
int l = strlen(s);
if(l % (l - next[l]) == 0) {
ans = l / (l - next[l]);
}
printf("%d\n", ans);
}
}