ac自动机入门

用处

用来统计字典中出现在字符串中的单词的个数,区分于
字符串中出现在字典中单词的个数。

两种情况都可以用ac自动机解决,区别在于第一种情况直接使用ac自动机,第二种情况使用ac自动机加上bit维护。

思想

保证部分后缀和前缀相同,防止重复匹配问题。

算法实现

1.建立trie树
普通的字典树
2.实现fail指针
指向父亲节点失败时指向节点的儿子节点。
3.询问
失败指向fail指针

ac自动机模板


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn =  2*1e6+9;

int trie[maxn][26]; //字典树
int cntword[maxn];  //记录该单词出现次数
int fail[maxn];     //失败时的回溯指针
int cnt = 0;

void insertWords(string s){
    int root = 0;
    for(int i=0;iq;
    for(int i=0;i<26;i++){      //将第二层所有出现了的字母扔进队列
        if(trie[0][i]){
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }

//fail[now]    ->当前节点now的失败指针指向的地方
tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
    while(!q.empty()){
        int now = q.front();
        q.pop();

        for(int i=0;i<26;i++){      //查询26个字母
            if(trie[now][i]){
                //如果有这个子节点为字母i+'a',则
//让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
                //有点绕,为了方便理解特意加了括号

                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else//否则就让当前节点的这个子节点
                //指向当前节点fail指针的这个子节点
                trie[now][i] = trie[fail[now]][i];
        }
    }
}


int query(string s){
    int now = 0,ans = 0;
    for(int i=0;i> n;
    for(int i=0;i> s ;
        insertWords(s);
    }
    fail[0] = 0;
    getFail();
    string t;
    cin >> s ;
    cout << query(s)<< endl;
    cin >> t;
    cout<< query(t)
    return 0;
}

第二种情况解法

/*
#include 

using namespace std;
typedef long long ll;

const int maxn = 2e6+100;
const int maxm = 1e5+10;

int pos[maxm*2],end_1[maxn];
ll bit[maxn];
char s[maxm];
string st[maxm];
int tot,num;
vectorG[maxn];
int vis[maxn],op[maxm];

struct Ac{
    int next[maxn][26];
    int fail[maxn];
    int root;
    //创建新rie树节点
    int newnode(){
        for(int i=0;i<26;i++)next[tot][i]=-1;
        tot++;
        return tot-1;
    }
    //初始化trie树
    void init(){
        tot=0;
        root=newnode();
    }
    //增加节点
    void add(char s[],int ind){
        int p=0;
        int len=strlen(s);
        for(int i=0;i Q;
        fail[0]=0;
        int p=0;
        for(int i=0;i<26;i++){
            if(next[p][i]!=-1){
                fail[next[p][i]]=0;
                Q.push(next[p][i]);
            }
            else next[p][i]=0;
        }
        while(!Q.empty()){
            int now=Q.front(); Q.pop();
            for(int i=0;i<26;i++){
                if(next[now][i]!=-1){
                    fail[next[now][i]]=next[fail[now]][i];
                    end_1[next[now][i]] = end_1[fail[next[now][i]]]||end_1[next[now][i]];
                    Q.push(next[now][i]);
                }
                else{
                    next[now][i]=next[fail[now]][i];
                }
            }
        }
    }
}ac;

int in[maxn],out[maxn];

void dfs(int u){
//    cout<0){
        res+=bit[x];
        x-= x&-x;
    }
    return res;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        ac.init();
        memset(end_1,0,sizeof(end_1));
        memset(vis,0,sizeof(vis));
        num=0;
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            ac.add(s,i);
        }
        for(int i=1;i<=m;i++){
            scanf("%d %s",&op[i],s);
            if(op[i]==1){
                ac.add(s,n+i);
            }
            else{
                st[i].assign(s);
            }
        }
        //1.建立ac自动机
        ac.build();
        //2.不知道
        for(int i=0;i

你可能感兴趣的:(算法)