AC自动机
Trie+Fail指针,通常配合动归出现在比较明显的字符串题中。
几个注意点:
1.bfs建图时先将所有fail指针指向根(0号节点),一开始应将根的所有儿子放入队列而不是根本身。
2.模板使用的方法是建立trie图而不是树,注意处理。
3.fail指针构成一棵树(显然),解题时常利用这个性质。
4.直接按照trie的方法建树,然后按bfs序建图。
5.常用于多串匹配,技巧是现考虑暴力再考虑用自动机性质优化复杂度。
void ins(char s[]){ int l=strlen(s),x=0; rep(i,0,l-1){ int t=s[i]-‘A‘+1; if (!ch[x][t]) clear(++cnt),ch[x][t]=cnt; x=ch[x][t]; } w[x]=1; } void build(){ int st=1,ed=0; rep(i,1,26) if (ch[0][i]) q[++ed]=ch[0][i]; while (st!=ed+1){ int x=q[st++]; rep(i,1,26) if (ch[x][i]) q[++ed]=ch[x][i],fail[ch[x][i]]=ch[fail[x]][i]; else ch[x][i]=ch[fail[x]][i]; w[x]|=w[fail[x]]; } }
后缀自动机
http://blog.csdn.net/doyouseeman/article/details/52245413
与后缀树有关联,暂时不会。
相当于将一个串的所有后缀放入一个trie然后将几个出现位置(右断点)相同的串压缩到同一个节点
稍难理解,注意点:
1.之所以能处理子串问题,本质是利用了“子串就是某个后缀的前缀”
2.一个节点存储几个Right集合相同的串(一定互为后缀),但也可以认为它只表示一个串(从根走到这个节点就相当于只走到这一个串上)
3.x代表的所有串后接一个c之后的Right集合与son[x][c]Right集合完全相同,而fa[x]代表的集合包含x代表的集合(且是最小的包含x代表的集合的节点)。
4.从根发出所有路径构成了原串的所有子串。每个节点代表子串的长度范围是( mx[fa[x]] , mx[x] ]。
5.走son寻找子串,走fa传递信息。两节点代表的集合若非包含关系则必然不相交。
主要应用于大量子串处理问题上,也可配合map等骗分。
void ext(int c){ int p=lst,np=lst=++cnt; mx[np]=mx[p]+1; l[np]=r[np]=mx[np]; while (!son[p][c] && p) son[p][c]=np,p=fa[p]; if (!p) fa[np]=1; else{ int q=son[p][c]; if (mx[p]+1==mx[q]) fa[np]=q; else{ int nq=++cnt; mx[nq]=mx[p]+1; memcpy(son[nq],son[q],sizeof(son[q])); fa[nq]=fa[q]; fa[np]=fa[q]=nq; while (son[p][c]==q) son[p][c]=nq,p=fa[p]; } } }
广义后缀自动机,多个串放入同一个SAM,每次lst赋值为根,暂不熟练。
https://www.cnblogs.com/candy99/p/sam.html
后缀数组
应用范围比后缀自动机广,但模板很容易忘记,常配合height解题。
http://www.cnblogs.com/candy99/p/6666617.html
注意:模板背熟,有时可以配合并查集使用,常用二分和按照height数组大小分组的方法。
struct sa_array{ int sa[N],rk[N],h[N],st[N][16],x[N],y[N],c[N]; char S[N]; int Cmp(int a,int b,int l){ return y[a]==y[b] && y[a+l]==y[b+l]; } void build_sa(int m){ memset(y,0,sizeof(y)); rep(i,0,m) c[i]=0; rep(i,1,n) c[x[i]=S[i]-‘a‘+1]++; rep(i,1,m) c[i]+=c[i-1]; for (int i=n; i; i--) sa[c[x[i]]--]=i; for (int k=1,p=0; p<n; k<<=1,m=p){ p=0; rep(i,n-k+1,n) y[++p]=i; rep(i,1,n) if (sa[i]>k) y[++p]=sa[i]-k; rep(i,0,m) c[i]=0; rep(i,1,n) c[x[y[i]]]++; rep(i,1,m) c[i]+=c[i-1]; for (int i=n; i; i--) sa[c[x[y[i]]]--]=y[i]; rep(i,1,n) y[i]=x[i]; p=1; x[sa[1]]=1; rep(i,2,n) x[sa[i]]=Cmp(sa[i-1],sa[i],k) ? p : ++p; } } void get(){ int k=0; rep(i,1,n) rk[sa[i]]=i; rep(i,1,n){ for (int j=sa[rk[i]-1]; i+k<=n && j+k<=n && S[i+k]==S[j+k]; k++); h[rk[i]]=k; if (k) k--; } } void rmq(){ rep(i,1,n) st[i][0]=h[i]; rep(i,1,log[n]) rep(j,1,n-(1<<i)+1) st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]); } int ask(int l,int r){ l++; int t=log[r-l+1]; return min(st[l][t],st[r-(1<<t)+1][t]); } int que(int x,int y){ return ask(min(rk[x],rk[y]),max(rk[x],rk[y]));} }SA;
KMP,Manacher较好理解,暂略。
扩展KMP一般可以用前述数据结构代替,还不会。
想不到用这些数据结构维护?用哈希!