ac自动机系列

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 40
int 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 __int64
int 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 100007

struct 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
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值