又一次写字符串
学KMP都是寒假的事了结果现在才写总结hhhhh
其实KMP在学的时候就听得迷迷糊糊的,但因为代码好记所以也算会写吧23333现在学exKMP也算对KMP有了更深的理解吧
那些现在不太理解的东西可以先放一放,过段时间再抱起来啃可能会好一些
关于KMP
主要其实就是对next数组的理解
next数组 记录的是字符串匹配过程中失配情况下可以向前多跳几个字符
贴一个博客:
KMP入门博客
感觉写的非常详细了
关于KMP的calcnext 完全匹配后对于j的处理
如果上个匹配处结束的字符串可以出现在下个匹配中,则j=nxt[j]
如果每个匹配与之前的匹配不允许有交叉的字符,则j=0
贴几道KMP的模板题吧
poj 3461
就是一道模板题吧,也没有什么题解
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define N 1000010
using namespace std;
int nxt[N],len1,len2,tot;
char s1[N],s2[N];
void calcnext(){
int i=0,j=-1;
nxt[0]=-1;
for(int i=0;i<len1;){
if(j==-1||s1[i]==s1[j]){
i++,j++;
nxt[i]=j;
}
else j=nxt[j];
}
}
void kmp(){
int i=0,j=0;
while(i<len1){
if(j==-1||s1[i]==s2[j]){
i++,j++;
if(j==len2){
tot++;
j=nxt[j];
}
}
else j=nxt[j];
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
tot=0;
memset(nxt,0,sizeof(nxt));
scanf("%s",s2);
scanf("%s",s1);
len1=strlen(s1);
len2=strlen(s2);
calcnext();
kmp();
printf("%d\n",tot);
}
return 0;
}
poj 2406
做这道题也是寒假了,那充满浓浓封建迷信气息的函数名23333
其实这道题主要就是对nxt数组的理解,计算有多少个循环节
【这道题的思想在hdu4333 一道求解exKMP的题中也有用到】
也算是积累一种求循环节的方法吧
#include<cstdio>
#include<cstring>
#define N 1000010
#define ms(x,y) memset(x,y,sizeof(x))
using namespace std;
char s[N];
int nxt[N],len;
void ac(){
int i=0,j=-1;
nxt[0]=-1;
while(i<len){
if(j==-1||s[i]==s[j]){
i++,j++;
nxt[i]=j;
}
else j=nxt[j];
}
}
int main(){
while(s[0]!='.'){
ms(nxt,0);
scanf("%s",s);
if(s[0]=='.') break;
len=strlen(s);
ac();
if(len%(len-nxt[len])==0){
printf("%d\n",len/(len-nxt[len]));
}
else printf("1\n");
}
return 0;
}
关于exKMP
也是贴一个讲的很详细的PPT
exKMP的详解
也贴一个模板题
hdu 4333
算是一个很水的题解吧:
主要是注意对循环节的处理(这个上面有贴)
然后对大于等于小于的数分别进行三种处理
如果该点的匹配程度大于等于原长,那么生成的这个新数肯定跟原数是一样的
其他情况下
比较它们失配的那一位的大小关系(肯定一次就可以比较出来了) 然后对应的值++
[其实真实写这道题的时候用不着这么复杂,但因为想存个板所以写的复杂了一些]
[这个板对应上面的PPT看可能会理解地更透彻]
//就当存个exKMP模板
#include<cstdio>
#include<cstring>
#define ms(x,y) memset(x,y,sizeof(x))
using namespace std;
const int N = 1000010;
int t;
char s1[N],s2[N*2];
int nxt[N];
int tot=0;
inline int Min(int a,int b){
return a<b?a:b;
}
void getnxt(){
int len=strlen(s1);
int i=0,j=-1;
nxt[0]=-1;
while(i<len){
if(j==-1||s1[i]==s1[j]){
i++,j++;
nxt[i]=j;}
else j=nxt[j];
}
}
//接下来是exkmp部分
int next[N],extand[N];
void getnext(){//起函数名废
int i=0,po=1,len=strlen(s1);
next[0]=len;//初始化nxt[0]
while(s1[i]==s1[i+1]&&i+1<len) i++;
next[1]=i;
for(int i=2;i<len;i++){
int p=po+next[po]-1,l=next[i-po];
if(i-1+l<p) next[i] = l;//情况一 可直接求解
else{
int j;
if(p-i+1>0) j=p-i+1;
else j=0;//如果i>po+next[po],则要从头开始匹配
while(i+j<len&&s1[j]==s1[j+i]) j++;//计算next[i]
next[i]=j;
po=i;
}
}
}
void getex(){
getnext();
int i=0,po=0;
int slen=strlen(s2),tlen=strlen(s1);
int minlen=Min(slen,tlen);
while(i<minlen&&s1[i]==s2[i]) i++;
extand[0]=i;
for(int i=1;i<slen;i++){
int p=po+extand[po]-1,l=next[i-po];
if(i-1+l<p) extand[i]=l;
else{
int j;
if(p-i+1>0) j=p-i+1;
else j=0;
while(i+j<slen&&j<tlen&&s2[j+i]==s1[j]) j++;
extand[i]=j;
po=i;
}
}
}
void update(){
ms(nxt,0);ms(next,0);ms(extand,0);ms(s2,0);
}
int main(){
scanf("%d",&t);
while(t--){
tot++;
update();
scanf("%s",s1);
int len=strlen(s1);
getnxt();
int temp,k=len-nxt[len];
if(len%k==0) temp=len/k;
else temp=1;//求循环节
for(int i=0;i<len;i++)
s2[i]=s2[i+len]=s1[i];
getex();
int a=0,b=0,c=0;
for(int i=0;i<len;i++){
if(extand[i]>=len) a++;
else if(s1[extand[i]]>s2[i+extand[i]]) b++;
else if(s1[extand[i]]<s2[i+extand[i]]) c++;
}
printf("Case %d: %d %d %d\n",tot,b/temp,a/temp,c/temp);
}
return 0;
}