ac自动机系列

来源:互联网 发布:数据库ip地址查询器 编辑:程序博客网 时间:2024/06/04 22:05

hdu 2234 考研路茫茫——单词情结

求长度不超过l的串中,包含一些单词的串的个数

首先构造自动机,然后把每个包含了终结态的状态都置为终结态,比如abc b,那么ab,abc,b都是终结态,

然后构造矩阵A,Ax就是长度为一时得到的结果A^n为长度为n结果

其中x=[0,1,.........0]第一个表示终结状态,第二个表示起始状态,最后一个表示为长度小于当前长度的终结态数量之和(终结态表示这个串包含了至少一个单词)

接着构造A

对自动机上任意一个状态转移u->v如果u是终结态就不转移,否则A[v][u]++,

然后就用矩阵快速幂来求解A^n

最后的答案就是A[0][1]+A[s][1]   ==== 因为只有x[1] =1其他=0,表示当前长度终结态数量+小于当前长度终结态数量 = 最后答案


#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 40int child[maxn][26],fail[maxn],state[maxn],flag[maxn];int cnt;void init(){    memset(child,0,sizeof(child));    memset(flag, 0,sizeof(flag));    memset(fail, 0,sizeof(fail));    cnt = 2;    for(int i = 0;i < 26;i++)        child[0][i] = 1;}char word[maxn];void add(int u,int i){    int x = word[i]-'a';    if(word[i]=='\0'){        flag[u] = 1;        return ;    }    if(child[u][x]==0)child[u][x] = cnt++;    add(child[u][x],i+1);}#define ll unsigned __int64int queue[100];void build(){    int front=0,rear=0;    queue[front++] = 1;    while(front != rear){        int u = queue[rear++];        flag[u] |= flag[fail[u]];        for(int i = 0;i < 26;i++){            if(child[u][i]==0) child[u][i] = child[fail[u]][i];            else {                int v = child[u][i];                fail[v] = child[fail[u]][i];                queue[front++] = v;            }        }    }}struct Matrix{    ll res[30][30];    ll n;    void init(){        memset(res,0,sizeof(res));    }};Matrix operator*(Matrix a,Matrix b){    Matrix c;    c.init();    c.n = a.n;    for(int i = 0;i < a.n;i++){        for(int j = 0;j < a.n ;j++)            for(int k = 0;k < a.n ;k++)                c.res[i][j]+=a.res[i][k]*b.res[k][j];    }    return c;}int main(){    ll n,l;    while(cin>>n>>l){        init();        for(int i = 0;i < n;i++){            cin>>word;            add(1,0);        }        build();        int s = 1;        for(int i = 1;i < cnt; i++)            if(flag[i]==0)state[i]=s++;            else state[i] = 0;        Matrix a;        a.init();        for(int i = 1;i < cnt; i++){            for(int j = 0;j < 26;j++){                int u = state[child[i][j]];                int v = state[i];                if(v == 0 ) continue;                a.res[u][v]++;            }        }        a.res[0][0] = 26;        a.res[s][0] = 1;        a.res[s][s] = 1;        Matrix ans;        ans.init();        ans.n = a.n = s+1;        for(int i = 0;i < 30;i++) ans.res[i][i] = 1;        while(l){            if(l&1) ans=a*ans;            a=a*a;            l/=2;        }        ll f = ans.res[s][1]+ans.res[0][1];        printf("%I64u\n",f);    }    return 0;}











hdu 2896 病毒侵袭

求一个串包含哪些字串,根据题目意思的话,就不用想太多了,不会有重叠情况的,

比如abc bc这种的。

解题:把字串建立ac自动机,是单词的终结态就标记为该单词的标号,

然后对于每个串走一遍自动机,如果这个标号没有出现过就加入ans

由于ans的大小<3 就直接判定就行了


#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<vector>#include<set>using namespace std;#define maxn 100007struct Node{    int child[128];    int flag;    Node*fail;};Node node[maxn];int cnt;int newNode(){    memset(node[cnt].child,-1,sizeof(node[cnt].child));    node[cnt].flag = 0;    return cnt++;}vector<int>ans;char word[maxn];int super,root;void insert(int u,int p,int flag){    if(word[p] == '\0'){        node[u].flag = flag;        return ;    }    int l = word[p];    if(node[u].child[l] == -1){        node[u].child[l] = newNode();    }    insert(node[u].child[l],p+1,flag);}int queue[maxn];void build(){    for(int i = 0;i < 128;i++)        node[super].child[i] = root;    node[root].fail = &node[super];    int front = 0,rear=0;    queue[front++] = root;    while(front > rear){        int u = queue[rear++];        for(int i = 0;i < 128;i++){            if(node[u].child[i] == -1){                node[u].child[i] = node[u].fail->child[i];            }            else {                node[node[u].child[i]].fail = &node[node[u].fail->child[i]];                queue[front++] = node[u].child[i];            }        }    }}void find(){    int len=strlen(word);    int u = root;    for(int i = 0;i < len; i++){        u = node[u].child[word[i]];        if(node[u].flag != 0){            int flag = 1;            for(int i = 0;i < ans.size() ;i++)                if(ans[i] == node[u].flag) flag = 0;            if(flag == 1) ans.push_back(node[u].flag);        }        if(ans.size() == 4)return ;    }}int main(){    int total,n;    while(scanf("%d",&n)!=EOF){        cnt = 0;        super = newNode();        root = newNode();        for(int i = 1;i <= n;i++){            scanf("%s",word);            insert(root,0,i);        }        build();        scanf("%d",&n);        int total = 0;        for(int i = 1;i <= n;i++){            scanf("%s",word);            ans.clear();            ans.push_back(0);            find();            if(ans.size() > 1){                sort(ans.begin(),ans.end());                printf("web %d:",i);                for(int j = 1;j < ans.size();j++)                    printf(" %d",ans[j]);                printf("\n");                total++;            }        }        printf("total: %d\n",total);    }    return 0;}



hdu 2222 Keywords Search

求一个串包含几个单词,

解法:构建ac自动机是单词结尾的标记一下

然后走ac自动机,走到一个状态访问fail节点,如果mark!=-就加上mark,然后mark=-1

这样经过的状态就不会重复计算了,


#include<iostream>#include<cstdio>#include<queue>#include<cstring>using namespace std;struct Node{    Node*ch[26],*fail;    int mark,id;}word[600001];int cnt;Node*newNode(){    memset(&word[cnt],0,sizeof(word[0]));    word[cnt].id = cnt;    return &word[cnt++];}void insert(Node*root,char*s){    if(*s==0){root->mark++;return;}    int id = *s-'a';    if(root->ch[id] == 0) root->ch[id] = newNode();    insert(root->ch[id],s+1);}Node *super,*root;void build(Node*root){    for(int i = 0;i < 26;i++)        super->ch[i] = root;    root->fail = super;    queue<Node*>Q;    Q.push(root);    while(!Q.empty()){        Node* q = Q.front();        Q.pop();        for(int i = 0;i < 26; i++){            if(q->ch[i] != NULL){                Q.push(q->ch[i]);                q->ch[i]->fail = q->fail->ch[i];                //cout<<(char)(i+'a')<<endl;            }            else q->ch[i] = q->fail->ch[i];        }    }}int autofind(char *s){    int ans = 0,id;    Node*rt = root,*p;    while(*s){        id = *s - 'a';        if(rt->ch[id] != root) rt = rt->ch[id];        else{            while(rt != root && rt->ch[id] == root)                rt = rt->fail;        }        p = rt;        while(p != root && p->mark != -1){            ans += p->mark;            p->mark = -1;            p = p->fail;        }        s++;    }    return ans;}char a[1000009];int main(){    int t,n;    scanf("%d",&t);    while(t--){        cnt = 0;        super = newNode();        root = newNode();        scanf("%d",&n);        while(n--){            scanf("%s",a);            insert(root,a);        }        build(root);        scanf("%s",a);        int ans = autofind(a);        printf("%d\n",ans);    }    return 0;}




0 0