hdu 2243 AC自動機+dp(矩陣快速冪優化)

做這個之前建議做一下poj 2778
這道題要求長度小於等於m的字符串包含所給串的有多少種,可以算出所有的情況然後減去不包含所給串的情況就是所求的。
大佬博客
大佬博客
矩陣裏面存的是從i節點到j節點走一步共有多少種走法(不能走題目上給的字符串)。
然後將這個矩陣m次冪就可以求出走m步(長度爲m的字符串)有多少種不包含做給串的字符串。小於等於m將其各個次冪加起來就好了,可以改一改矩陣一次就可以算出來。
所有情況就是26的1到m次冪加起來。

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL unsigned long long
using namespace std;
const int maxn=1e6+10;
struct zp
{
    LL arr[110][110];//代表從i節點走一步到j節點有多少種走法
} x;
int zlen;
zp jx(zp a,zp b)
{
    zp res;
    for(int i=0; i<zlen; i++)
        for(int j=0; j<zlen; j++)
            res.arr[i][j]=0;
    for(int i=0; i<zlen; i++)
        for(int j=0; j<zlen; j++)
            for(int k=0; k<zlen; k++)
                res.arr[i][j]=res.arr[i][j]+a.arr[i][k]*b.arr[k][j];
    return res;
}
zp jk(zp a,int b)
{
    zp res;
    for(int i=0; i<zlen; i++)
        for(int j=0; j<zlen; j++)
            res.arr[i][j]=(i==j);
    while(b)
    {
        if(b&1)
            res=jx(res,a);
        a=jx(a,a);
        b>>=1;
    }
    return res;
}
struct AC
{
    int node[maxn][26];
    int fail[maxn],cont,root,flag[maxn];
    int newnode()
    {
        for(int i=0; i<26; i++)
            node[cont][i]=-1;
        flag[cont++]=0;
        return cont-1;
    }
    void init()
    {
        cont=0;
        root=newnode();
    }
    void build(char a[])
    {
        int len=strlen(a);
        int now=root;
        for(int i=0; i<len; i++)
        {
            if(node[now][a[i]-'a']==-1)
                node[now][a[i]-'a']=newnode();
            now=node[now][a[i]-'a'];
        }
        flag[now]=1;
    }
    void get_fail()
    {
        queue<int> q;
        fail[root]=root;
        for(int i=0; i<26; i++)
        {
            if(node[root][i]==-1)
            {
                node[root][i]=root;
            }
            else
            {
                fail[node[root][i]]=root;
                q.push(node[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            for(int i=0; i<26; i++)
            {
                if(node[now][i]==-1)
                {
                    node[now][i]=node[fail[now]][i];
                    if(flag[fail[node[now][i]]]==1)//噹噹前節點的fail指向一個單詞結尾時這個單詞也是不可到達的
                        flag[node[now][i]]=1;
                }
                else
                {
                    fail[node[now][i]]=node[fail[now]][i];
                    q.push(node[now][i]);
                    if(flag[fail[node[now][i]]]==1)
                        flag[node[now][i]]=1;
                }
            }
        }
    }
    void buildMatrix()
    {
        memset(x.arr,0,sizeof(x.arr));
        for(int i=0; i<cont; i++)
            for(int j=0; j<26; j++)
                if(flag[node[i][j]]!=1&&flag[fail[node[i][j]]]!=1)
                    x.arr[i][node[i][j]]++;//這裏加加是因爲從同一個節點出發走一步可能有好幾種走法
                    for(int i=0;i<cont+1;i++)//要求矩陣x的一到m次冪所以要在後面加一列
                        x.arr[i][cont]=1;
    }
};
AC ac;
char s[2010];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        ac.init();
        for(int i=0; i<n; i++)
        {
            scanf("%s",s);
            ac.build(s);
        }
        ac.get_fail();
        ac.buildMatrix();
        zlen=ac.cont+1;
        x=jk(x,m);
        LL ans=0;
        for(int i=0;i<zlen;i++)
            ans+=x.arr[0][i];
        x.arr[0][0]=26,x.arr[0][1]=1;//矩陣求26^1+26^2......26
        x.arr[1][0]=0,x.arr[1][1]=1;
        zlen=2;
        x=jk(x,m);
        ans=x.arr[0][0]+x.arr[0][1]-ans;
        printf("%llu\n",ans);
    }
}
發佈了110 篇原創文章 · 獲贊 18 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章