AC自动机题集

AC自动机就是一种在 Trie树上的kmp,用于多模式串的匹配及对多模式串限制的dp。初始时将所有模式串放进Trie树中,然后在Trie树上构建next数组和fail数组。通过next可以进行转移,通过fail可以找到所有具有相同后缀的模式串。有了next数组,Trie树可以看成一个图。在这个图上可以进行各种dp,一般题目上规定了有很多字符串的限制的时候,应该就要想到AC自动机。

HDU-2222

复习AC自动机。

AC自动机是将多个模式串构建成一颗Trie,然后再Trie上构建fail指针。fail指针指向下一个后缀相同的串,复杂度是 O(n)

#include
using namespace std;
struct Trie
{
    int L,root,nxt[500007][26],end[500007],fail[500007];
    int newnode()
    {
        for(int i=0;i<26;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char buf[])
    {
        int len=strlen(buf);
        int now=root;
        for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
                nxt[now][buf[i]-'a']=newnode();
            now=nxt[now][buf[i]-'a'];
        }
        ++end[now];
    }
    void build()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0;i<26;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            for(int i=0;i<26;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    int query(char buf[])
    {
        int len=strlen(buf);
        int tmp,now=root,res=0;
        for(int i=0;i'a'];
            tmp=now;
            while(tmp!=root)
            {
                res+=end[tmp];
                end[tmp]=0;
                tmp=fail[tmp];
            }
        }
        return res;
    }
};
Trie t;
char s[1000007];
char pt[57];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        t.init();
        for(int i=0;iscanf("%s",pt);
            t.insert(pt);
        }
        t.build();
        scanf("%s",s);
        printf("%d\n",t.query(s));
    }
    return 0;
}

HDU - 2896

set 维护不同的编号

#include
using namespace std;
struct Trie
{
    int nxt[100007][128],L,root,fail[100007],end[100007];
    int newnode()
    {
        for(int i=0;i<128;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char buf[],int id)
    {
        int len=strlen(buf);
        int now=root;
        for(int i=0;iif(nxt[now][buf[i]]==-1)
                nxt[now][buf[i]]=newnode();
            now=nxt[now][buf[i]];
        }
        end[now]=id;
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<128;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<128;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    set<int> query(char buf[])
    {
        int len=strlen(buf);
        int now=root,tmp;
        set<int> res;
        for(int i=0;iwhile(tmp!=root)
            {
                if(end[tmp])
                    res.insert(end[tmp]);
                tmp=fail[tmp];
            }
        }
        return res;
    }
};

Trie t;
char pt[207];
char s[10007];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        t.init();
        for(int i=1;i<=n;++i)
        {
            scanf("%s",pt);
            t.insert(pt,i);
        }
        t.build();
        int m,ans=0;
        scanf("%d",&m);
        for(int i=1;i<=m;++i)
        {
            scanf("%s",s);
            auto res=t.query(s);
            if(res.size())
            {
                ++ans;
                printf("web %d:",i);
                for(auto k : res) printf(" %d",k);
                puts("");
            }
        }
        printf("total: %d\n",ans);
    }
    return 0;
}

HDU-3065

计数。。

#include
using namespace std;

int cnt[1007];
struct Trie
{
    int L,root,nxt[50007][128],fail[50007],end[50007];
    int newnode()
    {
        for(int i=0;i<128;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(string & buf , int id)
    {
        int now=root,len=buf.length();
        for(int i=0;iif(nxt[now][buf[i]]==-1)
                nxt[now][buf[i]]=newnode();
            now=nxt[now][buf[i]];
        }
        end[now]=id;
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<128;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<128;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    void query(string & buf)
    {
        int len=buf.length(),now=root,tmp;
        for(int i=0;iwhile(tmp!=root)
            {
                ++cnt[end[tmp]];
                tmp=fail[tmp];
            }
        }
    }
};
string pt[1007],s;
Trie t;
int main ()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin >> n)
    {
        t.init();
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;++i)
        {
            cin >> pt[i];
            t.insert(pt[i],i);
        }
        t.build();
        cin >> s;
        t.query(s);
        for(int i=1;i<=n;++i)
            if(cnt[i])
                cout << pt[i] << ": " << cnt[i] << '\n';
    }
    return 0;
}

ZOJ - 3430

先base64解码后再搞。。有个RE点就是解码后的字符是uchar的。

#include
using namespace std;
string b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int f[128];
int buf[100000];
int str[100000],strl;
void decode(string &s)
{
    int len=0;
    for(int i=0;iif(s[i]=='=')
        {
            len-=(s.length()-i+1)*2;
            break;
        }
        for(int j=0;j<6;++j)
            buf[len+j]=(f[s[i]]>>(6-j-1))&1;
        len+=6;
    }
    strl=0;
    for(int i=0;i8)
    {
        int k=0;
        for(int j=0;j<8;++j)
            k<<=1,k|=buf[i+j];
        str[strl++]=k;
    }
}
struct Trie
{
    int L,root,nxt[40000][256],fail[40000],end[40000],vis[40000];
    int newnode()
    {
        for(int i=0;i<256;++i)
            nxt[L][i]=-1;
        end[L]=vis[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(int buf[],int len)
    {
        int now=root;
        for(int i=0;iif(nxt[now][buf[i]]==-1)
                nxt[now][buf[i]]=newnode();
            now=nxt[now][buf[i]];
        }
        ++end[now];
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<256;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<256;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    int query(int buf[],int len ,int id)
    {
        int now=root,tmp,res=0;
        for(int i=0;iwhile(tmp!=root)
            {
                if(vis[tmp]!=id)
                    res+=end[tmp];
                vis[tmp]=id;
                tmp=fail[tmp];
            }
        }
        return res;
    }
};
Trie t;
int main()
{
//    ios::sync_with_stdio(false);
    for(int i=0;i<64;++i)
        f[b64[i]]=i;
    int n,m;
    while(cin >> n )
    {
        string s;
        t.init();
        for(int i=1;i<=n;++i)
        {
            cin >> s;
            decode(s);
            t.insert(str,strl);
        }
        t.build();
        cin >> m;
        for(int i=1;i<=m;++i)
        {
            cin >> s;
            decode(s);
            cout << t.query(str,strl,i) << '\n';
        }
        cout << '\n';
    }
    return 0;
}

POJ - 2778

考虑将所有串建AC自动机,然后Trie树上的next代表邻接表。

如果该点的end 不是1,即不是某个串的终止点,那么这个点是可以被转移的,转移方程为

dp[u]=edge(v,u) eixsitdp[v]

然后通过快速幂加速这个dp就行了。

#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 100000;
struct Matrix
{
    ll a[57][57];
    int n;
    Matrix(int _n)
    {
        n=_n;
        memset(a,0,sizeof(a));
    }
    ll* operator [](int index) { return a[index]; }
    Matrix operator * (Matrix &b)
    {
        Matrix c(n);
        for(int i=0;ifor(int k=0;kif(a[i][k])
                    for(int j=0;jreturn c;
    }
};
Matrix powm(Matrix a, int b)
{
    Matrix c(a.n);
    for(int i=0;ifor(int j=0;jwhile(b)
    {
        if(b&1) c=c*a;
        a=a*a;
        b>>=1;
    }
    return c;
}
struct Trie
{
    int nxt[57][4],fail[57],end[57],L,root;
    int newnode()
    {
        for(int i=0;i<4;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    int f(char ch)
    {
        if(ch=='A') return 0;
        else if(ch=='C') return 1;
        else if(ch=='G') return 2;
        else if(ch=='T') return 3;
    }
    void insert(char buf[])
    {
        int len=strlen(buf),now=root;
        for(int i=0;iif(nxt[now][f(buf[i])]==-1)
                nxt[now][f(buf[i])]=newnode();
            now=nxt[now][f(buf[i])];
        }
        end[now]=1;
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<4;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            if(end[fail[now]]) end[now]=1;
            for(int i=0;i<4;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    ll query(int b)
    {
        Matrix a(L);
        for(int i=0;ifor(int j=0;j<4;++j)
                if(!end[nxt[i][j]])
                    ++a[nxt[i][j]][i];
        a=powm(a,b);
        ll res=0;
        for(int i=0;i0])%mod;
        return res;
    }
}t;
char pt[50];
int main ()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        t.init();
        for(int i=0;iscanf("%s",pt);
            t.insert(pt);
        }
        t.build();
        printf("%I64d\n",t.query(m));
    }
    return 0;
}

HDU - 2243

快速幂的时候再维护一个求和变量就行了。。其他跟上题差不多。

#include
using namespace std;
typedef unsigned long long ll;
struct Matrix
{
    ll a[57][57];
    int n;
    Matrix(int _n)
    {
        n=_n;
        for(int i=0;ifor(int j=0;j0;
    }
    ll* operator [](int index) { return a[index]; }
    Matrix operator * (Matrix &b)
    {
        Matrix c(n);
        for(int i=0;ifor(int k=0;kif(a[i][k])
                    for(int j=0;jreturn c;
    }
};
Matrix powm(Matrix &a, ll b)
{
    Matrix c(a.n);
    for(int i=0;ifor(int j=0;jwhile(b)
    {
        if(b&1) c=c*a;
        a=a*a;
        b>>=1;
    }
    return c;
}
struct Trie
{
    int nxt[57][26],fail[57],end[57],L,root;
    int newnode()
    {
        for(int i=0;i<26;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char buf[])
    {
        int len=strlen(buf),now=root;
        for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
                nxt[now][buf[i]-'a']=newnode();
            now=nxt[now][buf[i]-'a'];
        }
        end[now]=1;
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<26;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            if(end[fail[now]]) end[now]=1;
            for(int i=0;i<26;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    ll query(ll b)
    {
        Matrix a(L+1);
        for(int i=0;ifor(int j=0;j<26;++j)
                if(!end[nxt[i][j]])
                    ++a[nxt[i][j]][i];
        for(int i=0;i<=L;++i) a[L][i]=1;
        a=powm(a,b+1);
        return a[L][0]-1;
    }
}t;
char pt[50];
ll getAll(ll b)
{
    Matrix c(2);
    c[0][0]=c[0][1]=1;
    c[1][1]=26;
    c=powm(c,b);
    return c[0][1]*26;
}
int main ()
{
    int n;ll m;
    ios::sync_with_stdio(false);
    while(cin >> n >> m)
    {
        t.init();
        for(int i=0;icin >> pt;
            t.insert(pt);
        }
        t.build();
        cout << getAll(m)-t.query(m) << '\n';
    }
    return 0;
}

HDU - 2825

dp[i][j] 表示第 i 个结点状态为 j 时的数量,然后转移就行。。。

#include
using namespace std;
const int mod = 20090717;
struct Trie
{
    int nxt[107][26],end[107],L,root,fail[107],dp[107][1024],tmp[107][1024];
    int newnode()
    {
        for(int i=0;i<26;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char buf[],int id)
    {
        int len=strlen(buf),now=root;
        for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
                nxt[now][buf[i]-'a'] = newnode();
            now=nxt[now][buf[i]-'a'];
        }
        end[now]|=(1<void build()
    {
        queue<int> q;
        for(int i=0;i<26;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            end[now]|=end[fail[now]];
            for(int i=0;i<26;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    int solve(int n,int m,int c)
    {
        for(int j=0;jfor(int s=0;s<(1<0;
        dp[0][0]=1;
        for(int i=0;ifor(int j=0;jfor(int s=0;s<(1<0;
            for(int j=0;jfor(int s=0;s<(1<if(dp[j][s])
                    {
                        for(int k=0;k<26;++k)
                        {
                            int nx=nxt[j][k];
                            tmp[nx][end[nx]|s]=(tmp[nx][end[nx]|s]+dp[j][s])%mod;
                        }
                    }
            for(int j=0;jfor(int s=0;s<(1<int res=0;
        for(int i=0;ifor(int j=0;j<(1<if(__builtin_popcount(j)>=c)
                    res=(res+dp[i][j])%mod;
        return res;
    }
}t;
char buf[20];
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        if(n==0&&m==0&&k==0) break;
        t.init();
        for(int i=0;iscanf("%s",buf);
            t.insert(buf,i);
        }
        t.build();
        printf("%d\n",t.solve(n,m,k));
    }
    return 0;
}

ZOJ - 3228

AC自动机常见的计数字符串题,不过有不能重叠的限制。可以考虑维护每种模式串上一次出现的位置 last ,若当前位置减上一次出现位置大于等于长度的话,这部分可以计数。

#include
using namespace std;
const int N=1e5+7;
map<string,int> mp;
string s;
int id[N],len[N],last[N],c1[N],c2[N],op[N];
struct Trie
{
    int L,root,nxt[N*6][26],fail[N*6],end[N*6];
    int newnode()
    {
        for(int i=0;i<26;++i) nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(string & buf, int id)
    {
        int now=root;
        for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
                nxt[now][buf[i]-'a']=newnode();
            now = nxt[now][buf[i]-'a'];
        }
        end[now]=id;
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<26;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<26;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    void query(string &buf)
    {
        int now=root,tmp;
        for(int i=0;i'a'];
            tmp=now;
            while(tmp!=root)
            {
                if(end[tmp])
                {
                    if(i-last[end[tmp]]>=len[end[tmp]])
                        ++c2[end[tmp]],last[end[tmp]]=i;
                    ++c1[end[tmp]];
                }
                tmp=fail[tmp];
            }
        }
    }
}t;
int main()
{
    ios::sync_with_stdio(false);
    int kase=1;
    string buf;
    while(cin >> s)
    {
        int n,cur=0;
        cin >> n;
        mp.clear();
        t.init();
        for(int i=0;icin >> op[i] >> buf;
            if(!mp.count(buf))
            {
                mp[buf]=++cur;
                len[cur]=buf.length();
                last[cur]=-10000;
                c1[cur]=c2[cur]=0;
                t.insert(buf,cur);
            }
            id[i]=mp[buf];
        }
        t.build();
        t.query(s);
        cout << "Case " << (kase++) << '\n';
        for(int i=0;iif(op[i]) cout << c2[id[i]] << '\n';
            else cout << c1[id[i]] << '\n';
        }
        cout << '\n' ;
    }
    return 0;
}

HDU - 3341

假如只有A和C这两个字母的话,那么很简单,直接状压转移就行,不过这里有4个字母,可以考虑根据每种字母的数量将其哈希成一种状态。状态数最大是 114=14641 。然后dp就行了。

#include
using namespace std;
int hs[41][41][41][41],dp[507][15000];
inline int Max(int a,int b) { return a > b ? a : b; }
struct Trie
{
    int L,root,nxt[507][4],fail[507],end[507];
    int newnode()
    {
        for(int i=0;i<4;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    int f(char ch)
    {
        if(ch=='A') return 0;
        else if(ch=='G') return 1;
        else if(ch=='C') return 2;
        else return 3;
    }
    void insert(char buf[])
    {
        int now=root,len=strlen(buf);
        for(int i=0;iif(nxt[now][f(buf[i])]==-1)
                nxt[now][f(buf[i])]=newnode();
            now = nxt[now][f(buf[i])];
        }
        ++end[now];
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<4;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            end[now]+=end[fail[now]];
            for(int i=0;i<4;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    int query(char buf[])
    {
        int cnt[4],len=strlen(buf);
        memset(cnt,0,sizeof(cnt));
        for(int i=0;iint all=0;
        for(int a=0;a<=cnt[0];++a)
            for(int b=0;b<=cnt[1];++b)
                for(int c=0;c<=cnt[2];++c)
                    for(int d=0;d<=cnt[3];++d)
                        hs[a][b][c][d]=all++;
        for(int i=0;ifor(int j=0;j1;
        dp[0][0]=1;
        for(int a=0;a<=cnt[0];++a)
            for(int b=0;b<=cnt[1];++b)
                for(int c=0;c<=cnt[2];++c)
                    for(int d=0;d<=cnt[3];++d)
                        for(int i=0;iif(dp[i][hs[a][b][c][d]]!=-1)
                            {
                                int id=hs[a][b][c][d];
                                if(a+1<=cnt[0]) dp[nxt[i][0]][hs[a+1][b][c][d]]=Max(dp[nxt[i][0]][hs[a+1][b][c][d]],dp[i][id]+end[nxt[i][0]]);
                                if(b+1<=cnt[1]) dp[nxt[i][1]][hs[a][b+1][c][d]]=Max(dp[nxt[i][1]][hs[a][b+1][c][d]],dp[i][id]+end[nxt[i][1]]);
                                if(c+1<=cnt[2]) dp[nxt[i][2]][hs[a][b][c+1][d]]=Max(dp[nxt[i][2]][hs[a][b][c+1][d]],dp[i][id]+end[nxt[i][2]]);
                                if(d+1<=cnt[3]) dp[nxt[i][3]][hs[a][b][c][d+1]]=Max(dp[nxt[i][3]][hs[a][b][c][d+1]],dp[i][id]+end[nxt[i][3]]);
                            }
        int res=0;
        for(int i=0;i1]);
        return res-1;
    }
}t;
char buf[50];
int main ()
{
    int n,kase=1;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        t.init();
        for(int i=0;iscanf("%s",buf);
            t.insert(buf);
        }
        t.build();
        scanf("%s",buf);
        printf("Case %d: %d\n",kase++,t.query(buf));
    }
    return 0;
}

HDU - 3247

首先求出各个资源串末尾之间的距离,要求不能有病毒串,所以走向病毒串的边不能走。因为各边为 1 ,因此跑一遍 bfs 就行了。然后作一遍状压dp,dp[i][j] 表示状态为 j ,且最后遇到的串是 j 时的消耗的最小字母数。

#include
using namespace std;
const int N=60007,INF=0x3f3f3f3f;
int d[10][10],dis[N],tail[10];
int Q[N];
struct Queue
{
    int head,tail;
    Queue() : head(0) , tail(0) {}
    bool empty(){return head==tail;}
    void push(int a){Q[tail++]=a;}
    void pop() { ++head; }
    int front() { return Q[head]; }
};
struct Trie
{
    int nxt[N][2],fail[N],end[N],rs[N],L,root,n;
    bool vis[N];
    int newnode()
    {
        nxt[L][0]=nxt[L][1]=-1;
        end[L]=0;
        rs[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert_rs(char buf[],int id)
    {
        int now=root,len=strlen(buf);
        for(int i=0;iif(nxt[now][buf[i]-'0']==-1)
                nxt[now][buf[i]-'0']=newnode();
            now=nxt[now][buf[i]-'0'];
        }
        rs[now]|=(1<void insert(char buf[])
    {
        int now=root,len=strlen(buf);
        for(int i=0;iif(nxt[now][buf[i]-'0']==-1)
                nxt[now][buf[i]-'0']=newnode();
            now=nxt[now][buf[i]-'0'];
        }
        end[now]=1;
    }
    void build()
    {
        Queue q;
        for(int i=0;i<2;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            rs[now]|=rs[fail[now]];
            end[now]|=end[fail[now]];
            for(int i=0;i<2;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    vector<int> tmp;
    vector<int> & pts(int k)
    {
        tmp.clear();
        for(int i=0;iif((k>>i)&1) tmp.push_back(i);
        return tmp;
    }
    void bfs(int st,int d[])
    {
//        puts("bfs");
        Queue q;
        for(int i=0;i0x3f3f3f3f;
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        dis[st]=0;
        for(int v : pts(rs[st]))
            d[v]=0;
        vis[st]=true;
        q.push(st);
        while(!q.empty())
        {
            int p=q.front();q.pop();
            int u=nxt[p][0];
            if(!end[u]&&!vis[u])
            {
                vis[u]=true;
                dis[u]=dis[p]+1;
                q.push(u);
                if(rs[u])
                    for(int v : pts(rs[u]))
                        d[v]=min(dis[u],d[v]);
            }
            u=nxt[p][1];
            if(!end[u]&&!vis[u])
            {
                vis[u]=true;
                dis[u]=dis[p]+1;
                q.push(u);
                if(rs[u])
                    for(int v : pts(rs[u]))
                        d[v]=min(dis[u],d[v]);
            }
        }
    }
}t;
char buf[50007];
int dp[10][1024];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0 || m==0) break;
        t.init();
        t.n=n;
        memset(dp,0x3f,sizeof(dp));
        for(int i=0;iscanf("%s",buf);
            t.insert_rs(buf,i);
            dp[i][1<strlen(buf);
        }
        for(int i=0;iscanf("%s",buf);
            t.insert(buf);
        }
        t.build();
        for(int i=0;ifor(int i=1;i<(1<for(int j=0;jfor(int k=0;kif(((i>>k)&1)^1)
                        dp[k][i|(1<1<int ans=INF;
        for(int i=0;i1<1]);
        printf("%d\n",ans);
    }
    return 0;
}

HDU - 4758

AC自动机上dp,dp[i][j][a][s] 表示第 i 个字母匹配到了自动机上第 j 个结点,消耗了 aR ,状态为 s 时的方案数。

#include
using namespace std;
const int mod = 1e9+7;
int Q[207];
struct Queue
{
    int head,tail;
    Queue() : head(0) , tail(0) {}
    bool empty(){return head==tail;}
    void push(int a){Q[tail++]=a;}
    void pop() { ++head; }
    int front() { return Q[head]; }
};
struct Trie
{
    int nxt[207][2],end[207],fail[207],dp[203][203][103][4],root,L;
    int newnode()
    {
        nxt[L][0]=nxt[L][1]=-1;
        end[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    int f(char ch){ return ch=='R' ? 0 : 1; }
    void insert(char buf[],int id)
    {
        int now=root,len=strlen(buf);
        for(int i=0;iif(nxt[now][f(buf[i])]==-1)
                nxt[now][f(buf[i])]=newnode();
            now=nxt[now][f(buf[i])];
        }
        end[now]=(1<void build()
    {
        Queue q;
        for(int i=0;i<2;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            end[now]|=end[fail[now]];
            for(int i=0;i<2;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    int query(int n,int m)
    {
        int i,j,a,s;
        for(i=0;i<=n+m;++i)
            for(j=0;jfor(a=0;a<=n;++a)
                    for(s=0;s<4;++s)
                        dp[i][j][a][s]=0;
        dp[0][0][0][0]=1;
        for(i=1;i<=n+m;++i)
            for(j=0;jfor(a=0;a<=n&&afor(s=0;s<4;++s)
                    {
                        int nx=nxt[j][0];
                        if(a1][s|end[nx]]=(dp[i][nx][a+1][s|end[nx]]+dp[i-1][j][a][s])%mod;
                        nx=nxt[j][1];
                        if(i-1-a1][j][a][s])%mod;
                    }
        int ans=0;
        for(i=0;i3])%mod;
        return ans;
    }
}t;
int main ()
{
    int T,n,m;
    scanf("%d",&T);
    char buf[207];
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        t.init();
        scanf("%s",buf);
        t.insert(buf,0);
        scanf("%s",buf);
        t.insert(buf,1);
        t.build();
        printf("%d\n",t.query(n,m));
    }
    return 0;
}

HDU - 4511

在AC自动机上dp找最短路。

dp[i][j] 表示第 i 个点在自动机上是第 j 个点的最小路径,只能向合法路径且点标号大的方向转移。因为从1开始,初始时将 dp[i][nxt[root][i]]=0

#include
using namespace std;
typedef double ld;
const ld INF = 1e20;
ld x[51],y[51],d[51][51];
struct Trie
{
    int nxt[507][51],fail[507],end[507],L,root,n;
    ld dp[51][507];
    int newnode()
    {
        for(int i=1;i<=n;++i)
            nxt[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init(int _n)
    {
        n=_n;
        L=0;
        root=newnode();
    }
    void insert(int buf[],int len)
    {
        int now=root;
        for(int i=0;iif(nxt[now][buf[i]]==-1)
                nxt[now][buf[i]]=newnode();
            now=nxt[now][buf[i]];
        }
        end[now]=1;
    }
    void build()
    {
        queue<int> q;
        for(int i=1;i<=n;++i)
        {
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else
            {
                fail[nxt[root][i]]=root;
                q.push(nxt[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            end[now]|=end[fail[now]];
            for(int i=1;i<=n;++i)
            {
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else
                {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    ld query()
    {
        for(int i=1;i<=n;++i)
            for(int j=i+1;j<=n;++j)
                d[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
        for(int i=1;i<=n;++i)
            for(int j=0;j1][nxt[root][1]]=0;
        for(int i=1;i<=n;++i)
            for(int j=0;jif(dp[i][j]!=INF)
                    for(int k=i+1;k<=n;++k)
                        if(!end[nxt[j][k]])
                            dp[k][nxt[j][k]]=min(dp[k][nxt[j][k]],dp[i][j]+d[i][k]);
        ld ans = INF;
        for(int i=0;ireturn ans;
    }
}t;
int buf[51];
int main ()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        for(int i=1;i<=n;++i)
            scanf("%lf%lf",&x[i],&y[i]);
        t.init(n);
        for(int i=0;iint k;
            scanf("%d",&k);
            for(int i=0;iscanf("%d",&buf[i]);
            t.insert(buf,k);
        }
        t.build();
        ld ans=t.query();
        if(ans==INF) puts("Can not be reached!");
        else printf("%.2f\n",ans);
    }
    return 0;
}

你可能感兴趣的:(AC自动机)