kmp: 求出模式串在文本串中第一次出现的位置 O(n+m)
扩展kmp: 求出模式串与文本串中每个i为下标的后缀串的最长公共前缀 extend[i] O(n+m)
manacher:求出母串中的最长回文子串O(n)
kmp模板:
#include<cstdio>//O(m + n) m模式串 n文本串
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<string>
using namespace std;
const int maxn=100005;
int nxt[maxn];
char x[maxn];
char y[maxn];
void getnext(char *p,int nxt[])
{
int plen=strlen(p);
nxt[0]=-1;
int k=-1;
int j=0;
while(j<plen-1)
{
if(k==-1||p[k]==p[j])
{
++k;
++j;
if(nxt[j]!=nxt[k])
nxt[j]=k;
else
nxt[j]=nxt[k];
}
else
{
k=nxt[j];
}
}
}
int kmpsearch(char *s, char *p)
{
int i=0;
int j=0;
int slen=strlen(s);
int plen=strlen(p);
while(i<slen&&j<plen)
{
if(j==-1||s[i]==p[j])
i++,j++;
else
j=nxt[j];
}
if(j==plen)return i-j;
else return -1;
}
int main()
{
scanf("%s",x);
scanf("%s",y);
getnext(y,nxt);
cout<<kmpsearch(x,y)<<endl;
return 0;
}
扩展kmp:
#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<string>
using namespace std;
const int maxn=100005;
int nxt[maxn];
int extend[maxn];
char s[maxn];
char t[maxn];
void getnext(char *s,int nxt[])
{
int nn = strlen(s);
nxt[0] = nn;
int p = 0;
while (p+1 < nn && s[p] == s[p+1]) p++;
nxt[1] = p;
int k = 1, L;
for (int i = 2; i < nn; i++)
{
p = k + nxt[k] - 1; L = nxt[i - k];
if (i + L <= p) nxt[i] = L;
else
{
int j = p - i + 1;
if (j < 0) j = 0;
while (i + j < nn && s[i + j] == s[j]) j++;
nxt[i] = j; k = i;
}
}
}
void getextend(char *s,char *t,int extend[])
{
int nn = strlen(s) ,mm = strlen(t);
getnext(s,nxt);
int p = 0;
while (p < nn && s[p] == t[p]) p++;
extend[0] = p;
int k = 0, L;
for (int i = 1; i < nn; i++)
{
p = k + extend[k] - 1;
L = nxt[i - k];
if (i + L <= p) extend[i] = L;
else
{
int j = p - i + 1;
if (j < 0) j = 0;
while (i + j < nn && s[i + j] == t[j]) j++;
extend[i] = j; k = i;
}
}
}
int main()
{
scanf("%s",s);
scanf("%s",t);
getnext(s,nxt);
getextend(s,t,extend);
int len=strlen(s);
for(int i=0;i<len;i++)
{
cout<<extend[i]<<endl;
}
return 0;
}
Manacher:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
using namespace std;
string a;
string Manacher(string s)
{
string res="$#";
for(int i=0;i<s.size();++i)
{
res+=s[i];
res+="#";
}
vector<int> P(res.size(),0);
int mi=0,right=0;
int maxLen=0,maxPoint=0;
for(int i=1;i<res.size();++i)
{
P[i]=right>i ?min(P[2*mi-i],right-i):1;
while(res[i+P[i]]==res[i-P[i]])
++P[i];
if(right<i+P[i])
{
right=i+P[i];
mi=i;
}
if(maxLen<P[i])
{
maxLen=P[i];
maxPoint=i;
}
}
return s.substr((maxPoint-maxLen)/2,maxLen-1);
}
int main()
{
char b[1010002];
//while(getline(cin,a)) 可以读空行
//while(~scanf("%s",b)) a=b 不可以读空行
//cout<<Manacher(a)<<endl; Manacher(a).length()
return 0;
}
第一题:求模式串在文本串中出现的次数
做法考虑一下next数组的意义,i对于母串,j对于模式串。如果i和j匹配成功,并且j是模式串的最后一个位置。
我们考虑j+1和i+1,j+1明显为空,i+1和j+1失配,这时我们只要知道j=nxt[j+1],就可以开始下一次的匹配。每次
j=plen的时候累计答案。
#include<cstdio>//O(m + n) m模式串 n文本串
#include<iostream>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<cstring>
#include<string>
using namespace std;
const int maxn=10005;
int nxt[maxn];
char x[1000006];
char y[maxn];
int ans=0;
void getnext(char *p,int nxt[])
{
memset(nxt,-1,sizeof(-1));
int plen=strlen(p);
nxt[0]=-1;
int k=-1;
int j=0;
while(j<plen)
{
if(k==-1||p[k]==p[j])
{
++k;
++j;
nxt[j]=k;
}
else
{
k=nxt[k];
}
}
}
int kmpsearch(char *s, char *p)
{
ans=0;
int i=0;
int j=0;
int slen=strlen(s);
int plen=strlen(p);
while(i<slen)
{
if(j==-1||s[i]==p[j])
i++,j++;
else
j=nxt[j];
if(j==plen)
{
ans++;
j=nxt[j];
}
}
return ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%s",y);
scanf("%s",x);
getnext(y,nxt);
cout<<kmpsearch(x,y)<<endl;
}
return 0;
}