信息学奥赛一本通 提高篇 提高版 第二部分 字符串算法 第2章 KMP算法
https://blog.csdn.net/starstar1992/article/details/54913261此文帮助读者快速了解KMP算法
https://blog.csdn.net/buppt/article/details/78531384此文帮助理解k=next[k]的回溯算法
https://blog.csdn.net/v_july_v/article/details/7041827此文帮助更深入的理解KMP算法
开始之前,先学习,可通过下面问题,学习KMP字符串匹配。
//P3375 【模板】KMP字符串匹配
//在线测评地址https://www.luogu.org/problemnew/show/P3375
//通用的做法,之后输出的结果,别忘了+1
//样例通过,提交,测试点11-13 WA。
//修改,int next[maxn];//之前写成 char next[maxn],错了11-13测试点。
//提交AC。2018-9-28
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char s1[maxn],s2[maxn];//此处写成 char s1[maxn],s2[maxn],next[maxn];
int next[maxn];//之前写成 char next[maxn],错了11-13测试点。
void calNext(char *p,int len){
int k=-1,q;
next[0]=-1;
for(q=1;q<len;q++){
while(k>-1&&p[q]!=p[k+1])k=next[k];
if(p[q]==p[k+1])k++;
next[q]=k;
}
}
void KMP(char *str,int len1,char *p,int len2){
int k=-1,i;
calNext(p,len2);
for(i=0;i<len1;i++){
while(k>-1&&str[i]!=p[k+1])k=next[k];
if(str[i]==p[k+1])k++;
if(k==len2-1){
printf(“%d\n”,(i-len2+1)+1);
k=-1;
i=i-len2+1;
}
}
}
int main(){
int len1,len2,i;
scanf(“%s%s”,s1,s2);
len1=strlen(s1);
len2=strlen(s2);
KMP(s1,len1,s2,len2);
for(i=0;i<len2;i++)
printf(“%d “,next[i]+1);
printf(“\n”);
return 0;
}
#10043 「一本通 2.2 例 1」剪花布条
//#10043. 「一本通 2.2 例 1」剪花布条
//在线测评地址https://loj.ac/problem/10043
//样例通过,提交AC。2018-10-1 23:24
//在线测评地址https://vjudge.net/problem/HDU-2087
//样例通过,提交AC。2018-10-1 23:29
#include <stdio.h>
#include <string.h>
#define maxn 1010
char s[maxn],p[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
int k=-1,i;
next[0]=-1;
for(i=1;i<len;i++){
while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
if(ptr[i]==ptr[k+1])k++;
next[i]=k;
}
}
void kmp(char *str,int len_s,char *ptr,int len_p){
int k=-1,i,cnt=0;
cal_next(ptr,len_p);//漏了该句
for(i=0;i<len_s;i++){
while(k>-1&&str[i]!=ptr[k+1])k=next[k];
if(str[i]==ptr[k+1])k++;
if(k==len_p-1){
k=-1;
cnt++;
}
}
printf(“%d\n”,cnt);
}
int main(){
int len_s,len_p;
while(scanf(“%s”,s)){
len_s=strlen(s);
if(len_s==1&&s[0]==’#’)break;
scanf(“%s”,p);
len_p=strlen(p);
kmp(s,len_s,p,len_p);
}
return 0;
}
#10044 「一本通 2.2 例 2」Power Strings
//#10044. 「一本通 2.2 例 2」Power Strings
//在线测评地址https://loj.ac/problem/10035
//在线测评地址https://vjudge.net/problem/POJ-2406
//猜测与next有关,简单模拟,但没有信心
//搜索网络,基本确认与next有关,重新模拟
//a
//-1
//1-(-1+1)=1
//aa
//-1 0
//2-(0+1)=1
//aaa
//-1 0 1
//3-(1+1)=1
//abca
//-1 -1 -1 0
//4-(0+1)=3
//abcab
//-1 -1 -1 0 1
//5-(1+1)=3
//基本确认,子字符串长度=n-(next[n]+1)
//思路:模拟+找规律。
//样例通过,提交80分,测试点2,5WA
//看了测试点2,才发现理解有错误
//aabaaba
//-1 0 -1 0 1 2 3
//7-(3+1)=3
//而答案却是7
//此文写得不错https://blog.csdn.net/zz_ylolita/article/details/50650394值得学习,修改,提交,WA,0分
//仔细一看,测试语句竟然未删除,
//删除,提交AC。2018-10-3 18:28
//此题的理解,建议,还是从模拟角度入手。
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char s[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
int k=-1,i;
next[0]=-1;
for(i=1;i<len;i++){
while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
if(ptr[i]==ptr[k+1])k++;
next[i]=k;
}
}
int main(){
int len,len_p,cnt;
while(scanf(“%s”,s)){
len=strlen(s);
if(len==1&&s[0]==’.’)break;
cal_next(s,len);
len_p=len-(next[len-1]+1);
if(len%len_p==0)cnt=len/len_p;
else cnt=1;//此处写成 cnt=len/len_p+1
printf(“%d\n”,cnt);
}
return 0;
}
#10045 「一本通 2.2 练习 1」Radio Transmission
//#10045. 「一本通 2.2 练习 1」Radio Transmission
//在线测评地址https://loj.ac/problem/10045
//此文讲解得不错https://blog.csdn.net/lingyan_BVB/article/details/81698527摘抄如下
//对一个长度为l的字符串来说,next[l]记录的就是他最长的相同的前缀与后缀,也就是说有next[l]的长度是重复出现的,那么除去这个前缀以后,剩下的就是这个字符串最小的循环节了,即l-next[l]
//样例通过,提交AC。2018-10-3 20:08
//该题,本已很接近题解了,只是next笔算不过关,
//该题,与 #10044. 「一本通 2.2 例 2」Power Strings 解法基本雷同。
#include <stdio.h>
#define maxn 1000100
char s[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
int i,k=-1;
next[0]=-1;
for(i=1;i<len;i++){
while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
if(ptr[i]==ptr[k+1])k++;
next[i]=k;
}
}
int main(){
int L;
scanf(“%d%s”,&L,s);
cal_next(s,L);
printf(“%d\n”,L-(next[L-1]+1));
return 0;
}
#10046 「一本通 2.2 练习 2」OKR-Periods of Words
//#10046. 「一本通 2.2 练习 2」OKR-Periods of Words
//在线测评地址https://loj.ac/problem/10046
//该题的难点,还是在于next数组的理解。2018-10-4 10:22 AC
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char s[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
int i,k=-1;
next[0]=-1;
for(i=1;i<len;i++){
while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
if(ptr[i]==ptr[k+1])k++;
next[i]=k;
}
}
int main(){
int len,i;
long long ans=0;
scanf(“%d%s”,&len,s);
cal_next(s,len);
for(i=0;i<len;i++)
if(next[i]>-1&&next[next[i]]>-1)
next[i]=next[next[i]];
for(i=0;i<len;i++)
if(next[i]>-1)
ans+=i-(next[i]);
printf(“%lld\n”,ans);
return 0;
}
#10047 「一本通 2.2 练习 3」似乎在梦中见过的样子
//#10047. 「一本通 2.2 练习 3」似乎在梦中见过的样子
//在线测评地址https://loj.ac/problem/10047
//https://blog.csdn.net/u012288458/article/details/47272493此文代码写得不做,可以借鉴
//该题难在样例的理解
//样例1简化版
//aaaa
//1
//aaa 第1种
//aaaa 第2种
// aaa 第3种
//共计3种
//样例2简化版
//abcabc
//2
//abcab 第1种
// bcabc 第2种
//共计2种
//KMP中j的两次初始化,要特别小心。样例通过,提交AC。2018-10-4 20:53
#include <stdio.h>
#include <string.h>
char s[15100];
int k,len,next[15100],ans=0;
void KMP(int p){
int i,j;
//j=p-1;//源自 p=0时,j=-1
next[p]=p-1;
for(i=p+1;i<len;i++){
j=next[i-1];//漏了此句
while(j>p-1&&s[i]!=s[j+1])j=next[j];
if(s[i]==s[j+1])j++;
next[i]=j;
}
j=next[p];//漏了此句
for(i=p+1;i<len;i++){
//j=p-1;
while(j>p-1&&s[i]!=s[j+1])j=next[j];
if(s[i]==s[j+1])j++;
while((j-p+1)*2>=(i-p+1))j=next[j];
if((j-p+1)>=k)ans++;
}
}
int main(){
int i;
scanf(“%s%d”,s,&k);
len=strlen(s);
for(i=0;i<len;i++)KMP(i);
printf(“%d\n”,ans);
return 0;
}
#10048 「一本通 2.2 练习 4」Censoring
//#10048. 「一本通 2.2 练习 4」Censoring
//在线测评地址https://loj.ac/problem/10048
//在线测评地址https://www.luogu.org/problemnew/show/P4824
//样例通过,提交AC。2018-10-4 15:50
//以下为程序执行过程,希望能有助于读者理解。
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char S[maxn],T[maxn],W[maxn];
int top=-1,next[maxn],sp[maxn];
void cal_next(char *ptr,int len){
int i,k=-1;
next[0]=-1;
for(i=1;i<len;i++){
while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
if(ptr[i]==ptr[k+1])k++;
next[i]=k;
}
}
void done(char *str,int lens,char *ptr,int lenp){
int i,j;
for(i=0;i<lens;i++){
W[++top]=str[i];
if(top>0)j=sp[top-1];//此处写成 if(top>=0)
else j=-1;
while(j>-1&&W[top]!=ptr[j+1])j=next[j];
if(W[top]==ptr[j+1])j++;
sp[top]=j;
if(sp[top]==lenp-1){//此处写成 if(top==lenp-1)
top-=lenp;
}
}
W[top+1]=0;
printf(“%s”,W);
}
int main(){
int len_S,len_T;
scanf(“%s%s”,S,T);
len_S=strlen(S),len_T=strlen(T);
cal_next(T,len_T);
done(S,len_S,T,len_T);
return 0;
}
2018-10-4 20:54 AC该节内容。该节的核心内容在于 next的理解及灵活应用。