kmp
next[i]:求模式串中前缀和后缀的最长公共前缀
即对于[1,i]来说,求最大的x,使s[1,x]等于s[i-x+1,i]
例题
以hdu1711为例,
统计a串中第一个出现b串的起始下标,
即若第一个出现b串的区间是[l,r],输出l
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[1000005],b[10005];
int n,m;
int nex[10005];
void kmppre(){
int i=0,j=nex[0]=-1;
while(i<m){
while(j!=-1&&b[i]!=b[j])j=nex[j];
nex[++i]=++j;
}
}
int kmpcount(){
int i,j,ans=0;
kmppre();
i=j=0;
while(i<n){
while(j!=-1&&a[i]!=b[j])j=nex[j];
i++;j++;
if(j==m)return i-j+1;
}
return -1;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i)scanf("%d",&a[i]);
for(int i=0;i<m;++i)scanf("%d",&b[i]);
printf("%d\n",kmpcount());
}
return 0;
}
最小表示法与最大表示法
最小表示法:
对于串bca来说,其循环串abc,bca,cab中字典序最小的那个,串abc,即为最小表示
最大表示法同理
hdu3374,用kmp求一个串的最小表示法
//一个串在表示法里出现了几次 等于这个串的循环节在串中出现的次数
//考虑一串珠子 往后转循环节个长度 还能得到一个与原串相同的串 这就是循环节的定义
//即循环节出现的次数 即为表示法中出现的次数
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<int,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
string s;
int net[1000005];
int getminmax(int flag,string s)//min是0 max是1 注意flag的应用
{
int n=s.size();
int i=0,j=1,k=0,t;
while(i<n&&j<n&&k<n)
{
t=s[(i+k)%n]-s[(j+k)%n];
if(!t)k++;
else
{
if(!flag)t>0?i+=k+1:j+=k+1;
else t>0?j+=k+1:i+=k+1;
if(i==j)j++;
k=0;
}
}
return i<j?i:j;
}
void kmppre(string s)
{
int i,j,n=s.size();
i=0,j=net[0]=-1;
while(i<n)
{
while(j!=-1&&s[i]!=s[j])j=net[j];
net[++i]=++j;
}
}
void init(int &t)
{
mem(net,0);
t=1;
}
int main()
{
int len,minn,maxx,t;
while(cin>>s)
{
init(t);
kmppre(s);
len=s.size();
minn=getminmax(0,s);//求最小表示法
maxx=getminmax(1,s);//求最大表示法
if(len%(len-net[len])==0)t=len/(len-net[len]);
printf("%d %d %d %d\n",1+minn,t,1+maxx,t);//minn的pos和maxx的pos
}
return 0;
}
循环节
设m为串长,则m-next[m]是一个循环节,
m-next[next[m]]也是循环节,以此类推…
可以不断跳next数组获取到所有循环节
这些循环节并不是都能整除m,
能整除m的最短循环节最为常用
扩展kmp(Z函数)
next[i]:求模式串中前缀和后缀的最长公共前缀
即对于[1,i]来说,求最大的x,使s[1,x]等于s[i-x+1,i]
extend[i]: 求文本串中以i开始的一个后缀,和模式串中的前缀的最长公共前缀
即,对于文本串t[1,m]和模式串s[1,n]来说,
求最大的x,使得t[i,i+x-1]等于s[1,x]
替代方案
kmp可以取巧替代exkmp,并且实际效果比kmp快
令t=s+'#'+t,即模式串在最前,中间用未出现的字符分隔,文本串在最后
则数组后半部分的next[i]即为所求
例题
以poj3080为例,
多个字符串,问你这几个字符串的最长公共子串是哪个,
如果有多个,输出字典序最大的那个,
如果最长的公共子串长度小于3,输出no significant commonalities
其实有点强行用exkmp的意思,暴力枚举每个串判是否在其他的串中存在,kmp也可以
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#define sci(x) scanf("%d",&(x))
#define scs(x) scanf("%s",(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
char a[15][65];
int net[65],ex[65];
char s1[65],s2[65],tmp[65];
void extkmppre(char s[])
{
int i=0,j,pos,len=strlen(s);
net[0]=len;
while(i+1<len&&s[i]==s[i+1])i++;
net[1]=i,pos=1;
rep(i,2,len-1)
{
if(net[i-pos]+i<net[pos]+pos)
{
net[i]=net[i-pos];
}
else
{
j=net[pos]+pos-i;
if(j<0)j=0;
while(i+j<len&&s[j]==s[i+j])j++;
net[i]=j,pos=i;
}
}
}
bool extkmp(char s1[],char s2[])
{
int i=0,j,pos,l1=strlen(s1),l2=strlen(s2);
extkmppre(s2);
while(i<l2&&i<l1&&s1[i]==s2[i])i++;
ex[0]=i,pos=0;
if(ex[0]==l2)return 1;
rep(i,1,l1-1)
{
if(net[i-pos]+i<ex[pos]+pos)
{
ex[i]=net[i-pos];
}
else
{
j=ex[pos]+pos-i;
if(j<0)j=0;
while(i+j<l1&&j<l2&&s1[i+j]==s2[j])j++;
ex[i]=j,pos=i;
}
if(ex[i]==l2)return 1;
}
return 0;
}
void cpy(char a[],char b[],int l,int r)
{
rep(i,l,r)a[i-l]=b[i];
a[r-l+1]='\0';
}
int main()
{
int t;
sci(t);
while(t--)
{
mem(a,0);
mem(tmp,0);
int m,maxlen=0,l,r;
sci(m);
rep(i,0,m-1)
scs(a[i]);
rep(i,0,59)
{
rep(j,i+2,59)
{
if(j-i+1<maxlen)continue;//剪枝
bool flag=1;
cpy(tmp,a[0],i,j);
rep(k,1,m-1)
{
if(extkmp(a[k],tmp))continue;
flag=0;
break;
}
if(flag)
{
if(j-i+1>maxlen)
{
maxlen=j-i+1;
l=i,r=j;
}
else if(j-i+1==maxlen)
{
rep(k,0,maxlen-1)
{
if(a[0][k+i]>a[0][k+l])break;
if(a[0][k+i]<a[0][k+l])
{
l=i,r=j;
break;
}
}
}
}
}
}
if(maxlen<3)puts("no significant commonalities");
else
{
cpy(tmp,a[0],l,r);
printf("%s\n",tmp);
}
}
return 0;
}