Keywords Search AC自动机QAQ

AC自动机,一直以来都以为是一个非常高大上的算法,其实它还真的挺高大上的。

首先来说,ac自动机的思想与kmp类似,需要自己模拟来理解。

给两个博客:

http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html

https://blog.csdn.net/KXL5180/article/details/88093307

还有bibi上有个挺好的视频可以看一下,有助于理解

板子分为3个部分来:

首先对于需要的数组

int cnt,root;//cnt表示树的某个节点位置;root表示根,其实就是0;
int fail[maxn];//表示上一个他这个字符的位置。
int ch[maxn][30];//用来表示一个新的节点,存的值是下一个字符的位置
int val[maxn];//表示某个节点的有效值,就是以该点串串结尾的个数

1.建树。建立字典树,这里开了静态的空间来装线段树。

int cnt,root; //N=26;
void init()
    {
        cnt=0;
        root=newnode();
    }

int newnode()//建立新的一个节点,并初始化fail指针与val值为0;
    {
        for(int i=0;i<N;i++)
            ch[cnt][i]=0;
        val[cnt]=fail[cnt++]=0;
        return cnt-1;
    }
void insert(char *s)//插入某一个字符串
    {
        int len=strlen(s);
        int u=0;
        for(int i=0;i<len;i++)
        {
            int v=s[i]-'a';
            if(!ch[u][v])//如果有该点就不开新节点
                ch[u][v]=newnode();//没有就开新节点
            u=ch[u][v];
        }
        val[u]++;//每当加完一个新的字符串,结尾其实就是节点要val值加1,表示这点是某个串串的结束
    }

2.建立fail指针。

fail指针的意义就是如果找一个点的时候,你可以找他的fail指针找到有相同作用的点,当某个点寻找下一点失败的时候有fail指针引导你下次应该跳转的位置。自己并不能说的很清楚。

可以参考博客:https://blog.csdn.net/u013371163/article/details/60469145

void getfail()
    {
        queue<int >q;
        int u=0;
        for(int i=0;i<N;i++)//找到连接root节点的点,加入队列fail指针已经是0了不用重新赋值。
            if(ch[u][i])
                q.push(ch[u][i]);
        while(!q.empty())//类似于bfs的搜索方式
        {
            u=q.front();
            q.pop();
            for(int i=0;i<N;i++)
            {
                if(ch[u][i])//如果u节点有下对应的(i+‘a’)
                {
                    fail[ch[u][i]]=ch[fail[u]][i];//这个点fail指针就是连接到他父亲的fail指针对应位置下的那个'a'+i字母的位置。
            //因为假设u点的fail的位置为v,那么v这个点的作用其实同u点,那么ch[v][i]即v下边如果有那个'a'+i字母,那么这个位置之前已经知道了,赋过去就行
            //如果没有呢,那么其实之前开辟新节点也是处理过的,fail[cnt][i]=0,那么他的fail指针就是指向0,就是根节点。
                    q.push(ch[u][i]);
                }
                else
                    ch[u][i]=ch[fail[u]][i];//如果u没有'a'+i这个字母,就把它的位置(注意是位置)直接跳到上边解释的位置,查询的时候模拟一下就知道了
            }
        }
    }

3.查找某个串的匹配串有几种。

int query(char *s)//查询的时候是需要自己模拟一下
    {
        int len=strlen(s);
        int u=0,ans=0;
        for(int i=0;i<len;i++)
        {
            int v=s[i]-'a';
            u=ch[u][v];//找到u下边的'a'+i字母的位置
            for(int j=u;j&&~val[j];j=fail[j])//fail指针走到根节点,或者某个点走过了
            {
                ans+=val[j];//加上某个节点的值,其实就是加上串尾点
                val[j]=-1;//标记为走过
            }
        }
        return ans;
    }

AC代码

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define inff 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define me(a,b) memset(a,b,sizeof(a))
#define min4(a,b,c,d) min(min(a,b),min(c,d))
#define min3(x,y,z) min(min(x,y),min(y,z))
#define max4(a,b,c,d) max(max(a,b),max(c,d))
#define max3(x,y,z) max(max(x,y),max(y,z))
typedef long long ll;
using namespace std;
const int maxn=5e5+5;
const int maxm=1e6+5;
const int N=26;
struct AC
{
    int cnt,root;
    int fail[maxn];
    int ch[maxn][30];
    int val[maxn];
    int newnode()
    {
        for(int i=0;i<N;i++)
            ch[cnt][i]=0;
        val[cnt]=fail[cnt++]=0;
        return cnt-1;
    }
    void init()
    {
        cnt=0;
        root=newnode();
    }
    void insert(char *s)
    {
        int len=strlen(s);
        int u=0;
        for(int i=0;i<len;i++)
        {
            int v=s[i]-'a';
            if(!ch[u][v])
                ch[u][v]=newnode();
            u=ch[u][v];
        }
        val[u]++;
    }
    void getfail()
    {
        queue<int >q;
        int u=0;
        for(int i=0;i<N;i++)//找到连接root节点的点,加入队列fail指针已经是0了不用重新赋值。
            if(ch[u][i])
                q.push(ch[u][i]);
        while(!q.empty())//类似于bfs的搜索方式
        {
            u=q.front();
            q.pop();
            for(int i=0;i<N;i++)
            {
                if(ch[u][i])//如果u节点有下对应的(i+‘a’)
                {
                    fail[ch[u][i]]=ch[fail[u]][i];//这个点fail指针就是连接到他父亲的fail指针对应位置下的那个'a'+i字母的位置。
            //因为假设u点的fail的位置为v,那么v这个点的作用其实同u点,那么ch[v][i]即v下边如果有那个'a'+i字母,那么这个位置之前已经知道了,赋过去就行
            //如果没有呢,那么其实之前开辟新节点也是处理过的,fail[cnt][i]=0,那么他的fail指针就是指向0,就是根节点。
                    q.push(ch[u][i]);
                }
                else
                    ch[u][i]=ch[fail[u]][i];//如果u没有'a'+i这个字母,就把它的位置(注意是位置)直接跳到上边解释的位置,查询的时候模拟一下就知道了
            }
        }
    }
    int query(char *s)//查询的时候是需要自己模拟一下
    {
        int len=strlen(s);
        int u=0,ans=0;
        for(int i=0;i<len;i++)
        {
            int v=s[i]-'a';
            u=ch[u][v];//找到u下边的'a'+i字母的位置
            for(int j=u;j&&~val[j];j=fail[j])//fail指针走到根节点,或者某个点走过了
            {
                ans+=val[j];//加上某个节点的值,其实就是加上串尾点
                val[j]=-1;//标记为走过
            }
        }
        return ans;
    }
}AC;
char str[maxm];
int main()
{
    int t,n;
    cin>>t;
    while(t--)
    {
        scanf("%d",&n);
        AC.init();
        while(n--)
        {
            scanf("%s",str);
            AC.insert(str);
        }
        AC.getfail();
        scanf("%s",str);
        printf("%d\n",AC.query(str));
    }
    return 0;
}

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值