多种颜色的方框同时框住了某个子串的右边。
st为模式串,s为匹配串。
void KMP(){
int i,j=0;
nx[1]=0;
fo(i,2,n){
while(st[j+1]!=st[i]&&j)j=nx[j];//跳不同颜色的方框
if(st[j+1]==s[i])j++;
p[i]=j;
}
j=0;
fo(i,1,n){
while(st[j+1]!=s[i]&&j)j=nx[j];//跳不同颜色的方框
if(st[j+1]==s[i])j++;
if(j==len){
//匹配成功
}
}
}
像KMP一样地,模式串先自己匹配自己一次,匹配完之后再和匹配串匹配一次。
EXKMP没有跳fail链的操作。
EXKMP解决的问题是:
用相同颜色的最大的方框去框住以 s [ 1 ] s[1] s[1]和 s [ i ] s[i] s[i]开始的子串,且这两个子串是相同的。
设 e x t [ i ] ext[i] ext[i]表示 s [ 1 ] s[1] s[1]和 s [ i ] s[i] s[i]的 L C P LCP LCP。
设 p = m a x ( i + e x t [ i ] − 1 ) p=max(i+ext[i]-1) p=max(i+ext[i]−1)且 p = i d + e x t [ i d ] − 1 p=id+ext[id]-1 p=id+ext[id]−1。
现在要求 e x t [ i ] ext[i] ext[i]。考虑什么东西是匹配好了的。
考虑将 s [ i d . . . p ] s[id...p] s[id...p]和 s [ 1... p − i d + 1 ] s[1...p-id+1] s[1...p−id+1]各用一个绿色的方框框起来,并用一个紫色的方框框住 s [ i . . . p ] s[i...p] s[i...p]和 s [ i − i d + 1... p − i d + 1 ] s[i-id+1...p-id+1] s[i−id+1...p−id+1].
现在已知2个紫色方框内的字符是一样的,所以欲知 e x t [ i ] ext[i] ext[i],就要看 e x t [ i − i d + 1 ] ext[i-id+1] ext[i−id+1]了。设 l = e x t [ i − i d + 1 ] l=ext[i-id+1] l=ext[i−id+1]。
用红色的方框框住 s [ i − i d + 1... i − i d + l ] s[i-id+1...i-id+l] s[i−id+1...i−id+l]和 s [ 1.. l ] s[1..l] s[1..l]。
那看看右边的红色方框的右端是否超过了 p p p(即: l > = p − i + 1 l>=p-i+1 l>=p−i+1)。如果没有,则可以立即确定 e x t [ i ] = l ext[i]=l ext[i]=l了,否则暴力维护一个指针,暴力判。
void Exkmp(){
int i,j,p,id,l;
j=1;next[1]=n;
while(j<n&&a[j]==a[j+1])j++;
next[2]=j-1;
id=2;
fo(i,3,n){
p=id+next[id]-1;l=next[i-id+1];
if(l<p-i+1)next[i]=l;
else{
j=p-i+1<1?1:p-i+1;
while(i+j-1<=n&&a[i+j-1]==a[j])j++;
next[i]=j-1;
id=i;
}
}
j=1;
while(j<n&&a[j]==b[j])j++;
exB[1]=j-1;
j=1;
while(j<n&&a[j]==b[j+1])j++;
exB[2]=j-2;
id=2;
fo(i,3,n){
p=id+exB[id]-1;l=next[i-id+1];
if(l<p-i+1)exB[i]=l;
else{
j=p-i+1<1:p-i+1;
while(i+j-1<=n&&b[i+j-1]==a[j])j++;
exB[i]=j-1;
id=i;
}
}
}
寻找最长回文子串。
同样也是个暴力,也是记录一个位置 p o s pos pos, m x = m a x ( i + r [ i ] ) mx=max(i+r[i]) mx=max(i+r[i])且 m x = p o s + r [ p o s ] mx=pos+r[pos] mx=pos+r[pos]。
目的:求 r [ i ] r[i] r[i]
如果跨不过去就确定了 r [ i ] r[i] r[i],否则 r [ i ] + + r[i]++ r[i]++暴力判。
void Manacher(){
int i;
ans=0;
scanf("%s\n",s);l=strlen(s);
fo(i,0,l-1){st[i*2]='#';st[i*2+1]=s[i];}st[l*2]='#';
l=l*2;r[0]=1;mx=pos=0;
fo(i,1,l){
if(i<mx)r[i]=min(r[pos*2-i],mx-i);
else r[i]=1;
while(st[i-r[i]]==st[i+r[i]]&&i-r[i]>=0&&i+r[i]<=l)r[i]++;
if(i+r[i]-1>mx){
mx=i+r[i]-1;
pos=i;
}
ans=max(ans,r[i]-1);
}
}
这个东西用处很多,支持计算一个字符串在另外一个串中出现的次数。
其实就是 T r i e Trie Trie上的 K M P KMP KMP,即如果 f a i l fail fail链上有当前字母的儿子,就退,并且连上去。
最后效果是怎样的?
若将从根走到 f a i l fail fail链上的所有点的路径代表的字符串拿出来,从长到短,右对齐排列好后,就会发现每一列上的字符是相同的。
不要忘记开队列!!!
void Aho_Corasick(){
fail[0]=-1;
Q.push(0);//Pay attention
while(!Q.empty()){
x=Q.front();Q.pop();
fo(i,0,25){
j=tr[x][i];
if(!j)continue;
Q.push(j);
k=fail[x];
while(~k&&!tr[k][i])k=fail[k];
if(k==-1)fail[j]=0;else fail[j]=tr[k][i];
}
}
}
bool comp(int *b,int x1,int x2,int len){
int x3=x1+len>=l?-1:b[x1+len];
int x4=x2+len>=l?-1:b[x2+len];
return b[x1]==b[x2]&&x3==x4;
}
void SA(){
int *A=a1,*B=b1,mx=26,len,tot;
fo(i,0,25)cnt[i]=0;
fo(i,0,l-1){
A[i]=s[i]-'a';
cnt[A[i]]++;
}
fo(i,1,25)cnt[i]+=cnt[i-1];
fd(i,l-1,0){//倒着for循环,使得相同排名的后缀按照标号排序
cnt[A[i]]--;
sa[cnt[A[i]]]=i;
}
for(len=1;len<=l;len<<=1){
tot=0;
fo(i,l-len,l-1)B[tot++]=i;//后缀s[l-len]~s[l-1]的后半部分啥都没有
fo(i,0,l-1)
if(sa[i]>=len)B[tot++]=sa[i]-len;//之前排名i的后缀为sa[i],现在标号为sa[i]-len的后缀应该排到len+i
fo(i,0,mx-1)cnt[i]=0;
fo(i,0,l-1)cnt[A[B[i]]]++;//开始按照B[i]排序了。
fo(i,1,mx-1)cnt[i]+=cnt[i-1];
fd(i,l-1,0){
cnt[A[B[i]]]--;
sa[cnt[A[B[i]]]]=B[i];
}
swap(A,B);
mx=1;
A[sa[0]]=0;
fo(i,1,l-1)
if(comp(B,sa[i],sa[i-1],len))A[sa[i]]=mx-1;
else A[sa[i]]=mx,mx++;//产生了分歧
if(mx==l)break;
}
}
void HEIGHT(){
int i,j,k=0;
fo(i,0,l-1)rank[sa[i]]=i;
height[0]=0;
fo(i,0,l-1)
if(rank[i]){
j=sa[rank[i]-1];
if(k)k--;
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}//性质:height[rank[i]]>=height[rank[i]-1]-1
}
int ins(int x,int wz){
int i,np=++gs,p=wz,nq,q;
Len[np]=Len[p]+1;sum[np]++;
while(p&&!sam[p][x])sam[p][x]=np,p=fail[p];
if(!p){fail[np]=1;return np;}
q=sam[p][x];
if(Len[q]==Len[p]+1)fail[np]=q;else{
nq=++gs;
fo(i,0,25)sam[nq][i]=sam[q][i];
fail[nq]=fail[q];
fail[np]=fail[q]=nq;
Len[nq]=Len[p]+1;
while(p&&sam[p][x]==q)sam[p][x]=nq,p=fail[p];
}
return np;
}
别忘记SAM要和数据结构扯上关系!
比如主席树维护 r i g h t right right集。
void update(int ps){
tr[ps].sum=tr[tr[ps].ls].sum+tr[tr[ps].rs].sum;
}
void change(int &px,int py,int l,int r,int x){
if(!px)px=++tot;
tr[px]=tr[py];
tr[px].sum++;
if(l==r)return;
int wz=(l+r)>>1;
if(x<=wz)change(tr[px].ls,tr[py].ls,l,wz,x);
else change(tr[px].rs,tr[py].rs,wz+1,r,x);
update(px);
}
int merge(int px,int py){
if(!px||!py)return px+py;
int nx=++tot;
tr[nx].sum=tr[px].sum+tr[py].sum;
tr[nx].ls=merge(tr[px].ls,tr[py].ls);
tr[nx].rs=merge(tr[px].rs,tr[py].rs);
return nx;
}
int query(int ps,int l,int r,int x,int y){
if(!ps)return 0;
if(l==x&&r==y)return tr[ps].sum;
int wz=(l+r)>>1;
if(y<=wz)return query(tr[ps].ls,l,wz,x,y);else
if(x>wz)return query(tr[ps].rs,wz+1,r,x,y);else
return query(tr[ps].ls,l,wz,x,wz)+query(tr[ps].rs,wz+1,r,wz+1,y);
}
int ins(int x,int wz){
int j,np=++gs,nq,p=wz,q;
Len[np]=Len[p]+1;
while(p&&!sam[p][x])sam[p][x]=np,p=fail[p];
if(!p)fail[np]=1;else{
q=sam[p][x];
if(Len[q]==Len[p]+1)fail[np]=q;else{
nq=++gs;
fo(j,0,25)sam[nq][j]=sam[q][j];
fail[nq]=fail[q];
fail[np]=fail[q]=nq;
Len[nq]=Len[p]+1;
while(p&&sam[p][x]==q)sam[p][x]=nq,p=fail[p];
}
}
change(rt[np],rt[1],1,n,i);
P[np]=i;
return np;
}