ACM常用模板——ac自动机

大三了,在网络赛结束那刻决定放弃搞算法了,做了一个的安卓,却被告知暑假参加的ACM网络赛北京赛区成功进入现场赛,意外又惊喜,感觉付出都不是白费的,然后又滚回去刷题了,整理了一些模板,跟你们分享:

ac自动机

模型:我们给出5个单词,say,she,shr,he,her。给定字符串为yasherhs。问多少个单词在字符串中出现过。
模板:

//======================
// HDU 2222
// 求目标串中出现了几个模式串
//====================
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
struct Trie
{
    int next[500010][26],fail[500010],End[500010];
    int root,L;
    int newnode()
    {
        for(int i = 0;i < 26;i++)
            next[L][i] = -1;
        End[L++] = 0;
        return L-1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void Insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for(int i = 0;i < len;i++)
        {
            if(next[now][buf[i]-'a'] == -1)
                next[now][buf[i]-'a'] = newnode();
            now = next[now][buf[i]-'a'];
        }
        End[now]++;
    }
    void build()
    {
        queue<int>Q;
        fail[root] = root;
        for(int i = 0;i < 26;i++)
            if(next[root][i] == -1)
                next[root][i] = root;
            else
            {
                fail[next[root][i]] = root;
                Q.push(next[root][i]);
            }
        while( !Q.empty() )
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0;i < 26;i++)
                if(next[now][i] == -1)
                    next[now][i] = next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    int query(char buf[])//查询该串出现多少个模式串;
    {
        int len = strlen(buf);
        int now = root;
        int res = 0;
        for(int i = 0;i < len;i++)
        {
            now = next[now][buf[i]-'a'];
            int temp = now;
            while( temp != root )
            {
                res += End[temp];
                End[temp] = 0;
                temp = fail[temp];
            }
        }
        return res;
    }
    void debug()
    {
        for(int i = 0;i < L;i++)
        {
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],End[i]);
            for(int j = 0;j < 26;j++)
                printf("%2d",next[i][j]);
            printf("]\n");
        }
    }
};
char buf[1000010];
Trie ac;
int main()
{
    int T;
    int n;
    scanf("%d",&T);
    while( T-- )
    {
        scanf("%d",&n);
        ac.init();
        for(int i = 0;i < n;i++)
        {
            scanf("%s",buf);//模式串
            ac.Insert(buf);
        }
        ac.build();
        scanf("%s",buf);//主串
        printf("%d\n",ac.query(buf));
    }
    return 0;
}

hdu2296
题目:给出m个模式串,每个串有一定的分值,构造一个长度不超过n的串,使得分值最大,输出长度最小,字典序最小的串
明显的AC+DP,dp[i][j]表示长度为i的时候,在Trie上的第j个结点时的最大分值,path[i][j]表示状态(i,j)时的字典序最小的串。
当最大值为0的时候,输出的是空串,

include<iostream>
include<cstdio>
include<map>
include<cstring>
include<cmath>
include<vector>
include<algorithm>
include<set>
include<string>
include<queue>
define inf 1<<30
define M 60005
define N 10005
define maxn 300005
define eps 1e-10
define zero(a) fabs(a)<eps
define Min(a,b) ((a)<(b)?(a):(b))
define Max(a,b) ((a)>(b)?(a):(b))
define pb(a) push_back(a)
define mem(a,b) memset(a,b,sizeof(a))
define LL long long
define lson step<<1
define rson step<<1|1
using namespace std;
struct Trie
{
    Trie *next[26];
    Trie *fail;
    int isword,kind;
};
Trie *que[M],s[M];
int dp[55][N],idx;
int n,m,h[105];
char str[105][105];
string path[55][N];
Trie *NewNode()
{
    Trie *tmp=&s[idx];
    mem(tmp->next,NULL);
    tmp->isword=0;
    tmp->fail=NULL;
    tmp->kind=idx++;
    return tmp;
}
void Insert(Trie *root,char *s,int len,int k)
{
    Trie *p=root;
    for(int i=0; i<len; i++)
    {
        if(p->next[s[i]-'a']==NULL) p->next[s[i]-'a']=NewNode();
        p=p->next[s[i]-'a'];
    }
    p->isword=h[k];
}
void Bulid_fail(Trie *root)
{
    int head=0,tail=0;
    que[tail++]=root;
    root->fail=NULL;
    while(head<tail)
    {
        Trie *tmp=que[head++];
        for(int i=0; i<26; i++)
        {
            if(tmp->next[i])
            {
                if(tmp==root) tmp->next[i]->fail=root;
                else
                {
                    Trie *p=tmp->fail;
                    while(p!=NULL)
                    {
                        if(p->next[i])
                        {
                            tmp->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL) tmp->next[i]->fail=root;
                }
                if(tmp->next[i]->fail->isword) tmp->next[i]->isword+=tmp->next[i]->fail->isword;
                que[tail++]=tmp->next[i];
            }
            else if(tmp==root) tmp->next[i]=root;
            else tmp->next[i]=tmp->fail->next[i];
        }
    }
}
void slove()
{
    int ans=0;
    mem(dp,-1);
    dp[0][0]=0;
    for(int i=0;i<=n;i++) for(int j=0;j<idx;j++) path[i][j].clear();
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<idx;j++)
        {
            if(dp[i][j]==-1) continue;
            for(int k=0;k<26;k++)
            {
                int cur=s[j].next[k]->kind;
                if(dp[i][j]+s[cur].isword>dp[i+1][cur])
                {
                    dp[i+1][cur]=dp[i][j]+s[cur].isword;
                    path[i+1][cur]=path[i][j]+(char)(k+'a');
                }
                else if(dp[i][j]+s[cur].isword==dp[i+1][cur]&&path[i][j]+(char)(k+'a')<path[i+1][cur])
                {
                    path[i+1][cur]=path[i][j]+(char)(k+'a');
                }
            }
        }
    }
    for(int i=1;i<=n;i++) for(int j=0;j<idx;j++) ans=max(ans,dp[i][j]);
    if(ans==0) {puts("");return;}
    string str=" ";
    for(int i=1;i<=n;i++) for(int j=0;j<idx;j++) if(dp[i][j]==ans&&(str==" "||(path[i][j].size()<str.size()||(path[i][j].size()==str.size()&&path[i][j]<str)))) str=path[i][j];
    cout<<str<<endl;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        idx=0;
        Trie *root=NewNode();
        scanf("%d%d",&n,&m);
        for(int i=1; i<=m; i++)
            scanf("%s",str[i]);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&h[i]);
            Insert(root,str[i],strlen(str[i]),i);
        }
        Bulid_fail(root);
        slove();
    }
    return 0;
}

hdu2986病毒侵袭
要输出出现模式串的id,用end记录id就可以了。还有trie树的分支是128的

//============================================================================
// Name        : HDU.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
include <stdio.h>
include <string.h>
include <iostream>
include <algorithm>
include <queue>
using namespace std;
struct Trie
{
    int next[210*500][128],fail[210*500],end[210*500];
    int root,L;
    int newnode()
    {
        for(int i = 0;i < 128;i++)
            next[L][i] = -1;
        end[L++] = -1;
        return L-1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void insert(char s[],int id)
    {
        int len = strlen(s);
        int now = root;
        for(int i = 0;i < len;i++)
        {
            if(next[now][s[i]] == -1)
                next[now][s[i]] = newnode();
            now=next[now][s[i]];
        }
        end[now]=id;
    }
    void build()
    {
        queue<int>Q;
        fail[root] = root;
        for(int i = 0;i < 128;i++)
            if(next[root][i] == -1)
                next[root][i] = root;
            else
            {
                fail[next[root][i]] = root;
                Q.push(next[root][i]);
            }
        while(!Q.empty())
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0;i < 128;i++)
                if(next[now][i] == -1)
                    next[now][i] = next[fail[now]][i];
                else
                {
                    fail[next[now][i]] = next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    bool used[510];
    bool query(char buf[],int n,int id)
    {
        int len = strlen(buf);
        int now = root;
        memset(used,false,sizeof(used));
        bool flag = false;
        for(int i = 0;i < len;i++)
        {
            now = next[now][buf[i]];
            int temp = now;
            while(temp != root)
            {
                if(end[temp] != -1)
                {
                    used[end[temp]] = true;
                    flag = true;
                }
                temp = fail[temp];
            }
        }
        if(!flag)return false;
        printf("web %d:",id);
        for(int i = 1;i <= n;i++)
            if(used[i])
                printf(" %d",i);
        printf("\n");
        return true;
    }
};
char buf[10010];
Trie ac;
int main()
{
    int n,m;
    while(scanf("%d",&n) != EOF)
    {
        ac.init();
        for(int i = 1;i <= n;i++)
        {
            scanf("%s",buf);
            ac.insert(buf,i);
        }
        ac.build();
        int ans = 0;
        scanf("%d",&m);
        for(int i = 1;i <= m;i++)
        {
            scanf("%s",buf);
            if(ac.query(buf,n,i))
                ans++;
        }
        printf("total: %d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值