ac自动机是一种基于trie树的算法,其本质和kmp上的处理很相似。
trie树结构:https://blog.csdn.net/qq_38890926/article/details/81158021
kmp转移思路:https://blog.csdn.net/qq_38890926/article/details/81158132
ac自动机组要由三个部分组成:trie树的建立 fail指针的匹配 对ac自动机的询问
每次建立自动机会有一次初始化
ac自动机类
struct node //node结构体
{
int exist;
node* next[26],fail;
node() //初始化
{
exist=0;
fail=NULL;
memset(next,0,sizeof(next));
}
};
struct AC
{
node *root;
const int SIZE=26;
AC(){root=newnode();}
void init(){del(root);root=newnode();} //时间换空间
//void init(){root=newnode();} //空间换时间
node* newnode(){return new node;}
//int idx(char c){return c;} //size=128,可见字符在128以内
inline int idx(char c){return c-'a';} //size=26
void insert(char *s,int num) //插入字符串建树
{
node *u=root;
int len=strlen(s);
for(int i=0;inext[c]==NULL)u->next[c]=new node();
u=u->next[c];
}
u->exist=num;
/*其他操作,在每次插入字符串的结尾node上进行操作*/
}
queue q; //fail指针的构建,采用bfs
void build()
{
while(!q.empty())q.pop();q.push(root);
while(!q.empty())
{
node* u=q.front();q.pop();//u也有u指向fail位置的性质,fail形成的集合结点有相同性质
for(int i=0;inext[i]!=NULL) //新结点,先构建fail,再加入队列
{
if(u==root)u->next[i]->fail=root;//判断当前是否是根,指向根
else
{ //不是根节点往回遍历,处理和kmp相似
node *f=u->fail;
while(f!=root && f->next[i]==NULL)f=f->fail;
if(f->next[i]!=NULL)f=f->next[i];
u->next[i]->fail=f;
}
q.push(u->next[i]);
}
}
}
}
void query(char* s)
{
node *u=root;
int len=strlen(s);
for(int i=0;inext[c]==NULL)u=u->fail;
if(u->next[c]!=NULL)
{
u=u->next[c];
node* temp=u;
while(temp!=NULL)
{
if(temp->exist)
{
vec[tot]=temp->exist;
tot++;
}
temp=temp->fail;
}
}
}
}
void del(node* rt)
{
if(rt==NULL)return;
for(int i=0;inext[i]!=NULL)del(rt->next[i]);
delete rt;
}
};
AC ac;
struct node //node结构体
{
bool exist;
int next[55];
int fail;
node() //初始化
{
exist=0;
fail=0;
memset(next,0,sizeof(next));
}
};
struct AC
{
int tot;
node trie[505];
AC(){tot=1;memset(trie,0,sizeof(trie));}
void init(){tot=1;memset(trie,0,sizeof(trie));}
inline int idx(char c){return mc[c];} //size=26
void insert(char *s) //插入字符串建树
{
int u=1;
int len=strlen(s);
for(int i=0;i q; //fail指针的构建,采用bfs的方式
void build()
{
while(!q.empty())q.pop();
q.push(1);
while(!q.empty())
{
int u=q.front();q.pop();//u指向fail位置具有的性质u也有,即fail形成的集合结点有相同性质!!
for(int i=0;i
struct node //node结构体
{
bool exist;
int next[26],fail;
node() //初始化
{
exist=0;
fail=0;
memset(next,0,sizeof(next));
}
};
struct AC
{
int tot;
node trie[505];
AC(){init();}
void init(){tot=1;memset(trie,0,sizeof(trie));}
inline int idx(char c){return mp[c];} //size=26
void insert(char *s) //插入字符串建树
{
int u=0;
int len=strlen(s);
for(int i=0;i q; //bfs构建fail指针
void build()
{
while(!q.empty())q.pop();
for(int i=0;i<26;i++) //先处理根
{
int v=trie[0].next[i];
if(v==0)continue;
trie[v].fail=0;
q.push(v);
}
while(!q.empty())
{
int u=q.front();q.pop();//u指向fail位置具有的性质u也有!
for(int i=0;i<26;i++)
{
int v=trie[u].next[i];
if(v)
{
trie[v].fail=trie[trie[u].fail].next[i];
q.push(v);
}
else trie[u].next[i]=trie[trie[u].fail].next[i];//就是fail
}
}
}
void query(char *s)
{
}
};
AC ac;
在ac自动机上面进行dp,需要注意的事情:
1.由于构建fail的时候,下层的结点具有与其fail结点共有的性质,我们需要将fail节点的性质向下赋值。
2.dp的思想:将自动机上面的所有结点当作一种状态,记录处于当前状态的dp值,接下来可以进行字符串的转移,利用fail指针进行的转移来进行相关的dp。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include