好吧我承认学noi晚了,不过感谢我的同学DERIT帮助。
AC自动机
1,建立字典树
struct node{
int ch[27]; //记录子叶有无,没有为0
void init()//初始化,如果题目只有一组数据可以不写
{
for (int i=0;i<=26;i++)
ch[i]=0;
}
}
queue<int> q;
int tot=0;//tot is used to 给树的节点编号
int flag[100004],int fail[100004]; flag是记录每个tot对应的字母是不是一个单词的结尾,用来统计结果
void add(char x[]) //建树函数,x[]是要插入树的字符串
{
int now=0;//正在树的哪一层
int len=strlen(x);//strlen 比较慢,不要再循环中使用
for (int i=0;i<len;i++) //查看所插入的字符串的每个字符
{
int tmp=now;
if (!tre[now].ch[i]) //如果节点不存在,则新建
{
tre[now].ch[i]=++tot;//tot加一给这个节点编号
flag[tot]=0;
fail[now]=0;
tre[tot].init();//初始化过程
}
now=tre[now].ch[tmp];//更新now,开始下一个节点的建树
}
flag[now]++;//现在now是这个字符串的最后一位,flag++即以这个字符为结尾的单词多了一个,其实等于1就可以了
}//建树完成
2,求fail数组
PS:fail数组的性质(意义),fail数组代表什么呢?其含义是,如果fail[a]=b,那么从根节点到a的字符串包含了从根节点到b的字符串
inline void bfs()//求fail的函数
{
int now;
//宽搜的思路
for (int i=0;i<=25;i++)
if (tre[0].ch[i]!=0)
q.push(tre[0].ch[i];
while(!q.empty)
{
now=q,front();
for (int i=0;i<=25;i++)
{
if (tre[now].ch[i]!=0)//如果有这个子叶
{
fail[tre[now].ch[i]]=tre[fail[now]].ch[i];//等于这个子叶的父亲的fail的相同子叶
q.push(tre[now].ch[i]);
}
else tre[now].ch[i]=tre[fail[now]].ch[i];//否则直接更新树,因为没有了子叶,而且两个字符串等效,所以更新树可以减小检索次数,缩小复杂度
}
}
}
建树over
接着直接通过fail数组遍历搜索树即可,如果某个点的flag!=0,说明找到了单词,ans++即可