AC自动机

好吧我承认学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++即可



 










   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值