【模板】后缀自动机
#include<bits/stdc++.h>
using namespace std;
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
struct Node{
Node *ch[CHARSET_SIZE],*prt; //prt是后缀链接
int maxl; //当前节点表示的字串的最大长度
Node(int maxl=0):ch(),prt(NULL),maxl(maxl){}
int getMin(){return prt->maxl+1;} //v->min=v->prt->maxl+1
}*root,*last; //root表示parent树的根/起始节点 last表示整个母串
void init(){root=last=new Node;}
Node *extend(int c){ //扩展SAM
Node *u=new Node(last->maxl+1),*v=last; //u表示新的母串
for(;v&&!v->ch[c];v=v->prt)v->ch[c]=u; //将last的后缀连接路径上没有字符c出边的v连向u
if(!v){u->prt=root;} //如果v跳到了NULL 需要把u连向parent树的根
else if(v->ch[c]->maxl==v->maxl+1){
u->prt=v->ch[c]; //把u连接到trans(v,c)
}else{ //需要新建节点
Node *n=new Node(v->maxl+1),*o=v->ch[c]; //n是new o是old
copy(o->ch,o->ch+CHARSET_SIZE,n->ch); //复制出边到新节点
n->prt=o->prt; //n的后缀链接指向o的后缀连接
o->prt=u->prt=n; //o和u的后缀链接指向n
for(;v&&v->ch[c]==o;v=v->prt)v->ch[c]=n; //把路径上原来有转移的o的节点改成指向n
}
last=u; //替换整个母串
return u;
}
}sam;
int main(){
sam.init();
return 0;
}
如果你还需要计算right集合的大小,那么就加上内存池吧
#include<bits/stdc++.h>
using namespace std;
#define N 2000010
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
struct Node{
Node *ch[CHARSET_SIZE],*prt; //prt是后缀链接
int maxl,right; //maxl当前节点表示的字串的最大长度 right集合大小
Node(int maxl=0,int news=0):ch(),prt(NULL),maxl(maxl),right(news){}
int getMin(){return prt->maxl+1;} //v->min=v->prt->maxl+1
}*root,*last,pool[N],*cur; //root表示parent树的根/起始节点 last表示整个母串 内存池方便遍历
void init(){cur=pool;root=last=new (cur++)Node;}
void extend(int c){ //扩展SAM
Node *u=new (cur++)Node(last->maxl+1,1),*v=last;//u表示新的母串
for(;v&&!v->ch[c];v=v->prt)v->ch[c]=u; //将last的后缀连接路径上没有字符c出边的v连向u
if(!v){u->prt=root;} //如果v跳到了NULL 需要把u连向parent树的根
else if(v->ch[c]->maxl==v->maxl+1){
u->prt=v->ch[c]; //把u连接到trans(v,c)
}else{ //需要新建节点
Node *n=new (cur++)Node(v->maxl+1,0),*o=v->ch[c]; //n是new o是old
copy(o->ch,o->ch+CHARSET_SIZE,n->ch); //复制出边到新节点
n->prt=o->prt; //n的后缀链接指向o的后缀连接
o->prt=u->prt=n; //o和u的后缀链接指向n
for(;v&&v->ch[c]==o;v=v->prt)v->ch[c]=n; //把路径上原来有转移的o的节点改成指向n
}
last=u; //替换整个母串
}
vector<Node*> topo;
void toposort(){ //按照maxl从小到大排序
static int buc[N];
int maxv=0;
for(Node *p=pool;p!=cur;p++){
maxv=max(maxv,p->maxl);
buc[p->maxl]++;
}
for(int i=1;i<=maxv;i++)buc[i]+=buc[i-1];
topo.resize(cur-pool);
for(Node *p=pool;p!=cur;p++)topo[--buc[p->maxl]]=p;
fill(buc,buc+maxv+1,0); //清空
}
void cal_right(){
toposort();
for(int i=topo.size()-1;i>0;i--){ //递推right按照maxl从大到小
Node *v=topo[i];
v->prt->right+=v->right;
}
}
}sam;
int main(){
sam.init();
return 0;
}
如果你用不来指针或者不方便用指针,莫慌,这里有数组版
#include<bits/stdc++.h>
using namespace std;
#define N 2000010
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
struct Node{
int ch[CHARSET_SIZE],prt; //prt是后缀链接
int max; //当前节点表示的字串的最大长度
Node(int max=0):ch(),prt(0),max(max){}
}t[N];
int cnt,root,last; //root表示parent树的根/起始节点 last表示整个母串
int getMin(int u){return t[t[u].prt].max+1;} //v->min=v->prt->max+1
int newnode(int max=0){t[++cnt]=Node(max);return cnt;}
void init(){cnt=0;root=last=newnode();}
void extend(int c){ //扩展SAM
int u=newnode(t[last].max+1),v=last; //u表示新的母串
for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u; //将last的后缀连接路径上没有字符c出边的v连向u
if(!v){t[u].prt=root;} //如果v跳到了0 需要把u连向parent树的根
else if(t[t[v].ch[c]].max==t[v].max+1){
t[u].prt=t[v].ch[c]; //把u连接到trans(v,c)
}else{ //需要新建节点
int n=newnode(t[v].max+1),o=t[v].ch[c]; //n是new o是old
memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch)); //复制出边到新节点
t[n].prt=t[o].prt; //n的后缀链接指向o的后缀连接
t[o].prt=t[u].prt=n; //o和u的后缀链接指向n
for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n; //把路径上原来有转移的o的节点改成指向n
}
last=u; //替换整个母串
}
}sam;
int main(){
sam.init();
return 0;
}
数组+right集合大小维护
#include<bits/stdc++.h>
using namespace std;
#define N 2000010
const int CHARSET_SIZE=26;
struct Suffix_Automaton{
struct Node{
int ch[CHARSET_SIZE],prt; //prt是后缀链接
int maxl,right; //maxl当前节点表示的字串的最大长度 right集合大小
Node(int maxl=0,int news=0):ch(),prt(0),maxl(maxl),right(news){}
}t[N];
int cnt,root,last; //root表示parent树的根/起始节点 last表示整个母串 内存池方便遍历
int getMin(int u){return t[t[u].prt].maxl+1;} //v->min=v->prt->maxl+1
int newnode(int maxl=0,int news=0){t[++cnt]=Node(maxl,news);return cnt;}
void init(){cnt=0;root=last=newnode();}
void extend(int c){ //扩展SAM
int u=newnode(t[last].maxl+1,1),v=last; //u表示新的母串
for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u; //将last的后缀连接路径上没有字符c出边的v连向u
if(!v){t[u].prt=root;} //如果v跳到了0 需要把u连向parent树的根
else if(t[t[v].ch[c]].maxl==t[v].maxl+1){
t[u].prt=t[v].ch[c]; //把u连接到trans(v,c)
}else{ //需要新建节点
int n=newnode(t[v].maxl+1,0),o=t[v].ch[c]; //n是new o是old
memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch)); //复制出边到新节点
t[n].prt=t[o].prt; //n的后缀链接指向o的后缀连接
t[o].prt=t[u].prt=n; //o和u的后缀链接指向n
for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n; //把路径上原来有转移的o的节点改成指向n
}
last=u; //替换整个母串
}
}
int topo[N];
void toposort(){ //按照maxl从小到大排序
static int buc[N];
int maxv=0;
for(int i=1;i<=cnt;i++){
buc[t[i].maxl]++;
maxv=max(maxv,t[i].maxl);
}
for(int i=1;i<=maxv;i++)buc[i]+=buc[i-1];
for(int i=1;i<=cnt;i++)topo[buc[t[i].maxl]--]=i;
fill(buc,buc+maxv+1,0);
}
void cal_right(){
toposort();
for(int i=cnt;i>=1;i--){ //递推right按照maxl从大到小
Node v=t[topo[i]];
t[v.prt].right+=v.right;
}
}
}sam;
int main(){
sam.init();
return 0;
}