bzoj 1559 ac自動機 + dfs

這個dfs比較難 做成板子了


http://www.cnblogs.com/LadyLex/p/7514403.html

題目描述

衆所周知,密碼在信息領域起到了不可估量的作用。對於普通的登陸口令,唯一的破解 方法就是暴力破解一逐個嘗試所有可能的字母組合,但這是一項很耗時又容易被發現的工 作。所以,爲了獲取對方的登陸口令,在暴力破解密碼之前,必須先做大量的準備工作。經 過情報的蒐集,現在得到了若干有用信息,形如:

“我觀察到,密碼中含有字符串***。”

例如,對於一個10位的密碼以及觀察到的字符串hello與world,可能的密碼組合爲 helloworld與worldhello;而對於6位的密碼以及觀察到的字符串good與day,可能的 密碼組合爲gooday。

有了這些信息,就能夠大大地減少嘗試的次數了。請編一個程序,計算所有密碼組合的 可能。密碼中僅可能包含a - z之間的小寫字母。

輸入

輸入數據首先輸入兩個整數L,N,分別表示密碼的長度與觀察到子串的個數。 

接下來N行,每行若干個字符,描述了每個觀察到的字符串。

輸出

輸出數據第一行爲一個整數,代表了滿足所有觀察條件字符串的總數。

若這個數字小於等於42,則按字典順序輸出所有密碼的可能情況,每行一個,

否則,只輸出滿足所有觀察條件字符串的總數即可。

樣例輸入

10 2
hello
world

樣例輸出

2
helloworld
worldhello

提示

對於100%的數據,1<=L<=25,1<=N<=10,每個觀察到的字符串長不超過10,並且保 證輸出結果小於2^63。

 

好久沒打AC自動機上的DP了……我怎麼什麼題都不會做啊.jpg

這道題是我今天刷到的比較好的一道題……不僅考到了dp,還考到了搜索基本功。

我們首先考慮:對於串i和j,如果j是i的子串,那麼我們根本不用考慮最初單獨插入進來的j串,

因爲只要i串存在,j串就一定存在

那麼我們可以在構建出AC自動機之後,把每個節點從fail指針能達到的節點都設爲”不是單詞節點“,最後再給單詞節點重新編號即可。

那麼接下來,我們考慮dp的過程。由於節點數,串數和串長都很小,所以我們考慮狀態壓縮來解決這個問題。

我們定義狀態數組f[i][j][k]表示當前串長爲i,位於j號節點,模式串出現情況爲k的方案數。

(這種"走i步到達j節點”也是AC自動機上的常見套路之一)

那麼我們事先把單詞節點對應的串用二進制壓好,轉移到時候我們只需要這樣處理即可:

f[i+1][rt->ch[u]->id][k|rt->ch[u]->val]+=f[i][j][k]

這樣我們就可以搜出方案數,接下來我們考慮輸出小於42的具體方案。

首先我們可以得到一個性質:若總方案樹不超過42,那麼最終串一定僅由給定串拼接而成。

因爲如果隨機字母可以存在,哪怕只有1個模式串,並且僅有1個隨機字母,合法方案數在這種最小情況下也有2×26=52(>42)個

因此我們只需要用搜索進行一個dp的逆過程,看合法方案由哪個節點轉移過來,並且記錄一路上經過的字符,最後排序輸出即可。

這真是一道很不錯的題目,方式以及套路很經典,對於狀壓和搜索的應用都很靈活!

(Ps:我還打了一種利用狀壓從而不用去重的打法,放在最下面)

去重版代碼見下:



#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=12,L=30,K=(1<<10)+10;
typedef long long LL;
short l,n,len[N],bin[N],tot,cnt,num;
char s[N][N];
struct node
{
    node *ch[26],*f;short id,st,meaning;
    node(){memset(ch,0,sizeof(ch)),st=0,id=++cnt;}
}*null,*root,*q[N*N],*data[N*N];
inline node* newnode()
    {node *o=new node();data[cnt]=o;return o;}
inline void add(int x)
{
    node *rt=root;register int i,d;
    for(i=0;i<len[x];++i)
    {
        d=s[x][i]-'a';
        if(!rt->ch[d])rt->ch[d]=newnode();
        rt=rt->ch[d],rt->meaning=d;
    }
    rt->st=1;
}
inline void get_fail()
{
    register int i,hd=1,tl=0;
    node *rt,*u;
    for(i=0,root->f=root;i<26;++i)
        if(root->ch[i])root->ch[i]->f=root,q[++tl]=root->ch[i];
        else root->ch[i]=root;
    while(hd<=tl)
        for(rt=q[hd++],u=rt->f,i=0;i<26;++i)
            if(rt->ch[i])rt->ch[i]->f=u->ch[i],q[++tl]=rt->ch[i];
            else rt->ch[i]=u->ch[i];
    for(i=1;i<=cnt;++i)for(u=data[i]->f;u&&u!=null&&u!=root;u=u->f)u->st=0;
    for(i=1;i<=cnt;++i)if(data[i]->st)data[i]->st=++num;
    for(i=1;i<=cnt;++i)if(data[i]->st)data[i]->st=bin[num-data[i]->st];
}
LL f[L][N*N][K];
struct sol
{
    char s[L];
    sol(){memset(s,0,sizeof(s));}
    inline void print(){puts(s);}
    inline bool operator < (const sol &b) const
    {
        for(register int i=0;i<l;++i)
            if(s[i]!=b.s[i])return s[i]<b.s[i];
    }
}str[50],stack;
void dfs(int len,int id,int state,int now)
{
    register int i,j,k,st;
    stack.s[len-1]=now+'a';
    if(len==1){str[++tot]=stack;return;}
    for(j=2;j<=cnt;++j)
        if(f[len-1][j][state]&&data[j]->ch[now]->id==id)
            dfs(len-1,j,state,data[j]->meaning);
    if(data[id]->st)
        for(st=state^data[id]->st,j=2;j<=cnt;++j)
            if(f[len-1][j][st]&&data[j]->ch[now]->id==id)
                dfs(len-1,j,st,data[j]->meaning);
}
inline void get_solution()
{
    register int i,j;tot=0;
    for(j=2;j<=cnt;++j)
        if(f[l][j][bin[num]-1])
            dfs(l,j,bin[num]-1,data[j]->meaning);
}
int main()
{
    scanf("%d%d",&l,&n);root=newnode();
    register int i,j,k,u,v;node *rt;LL ans=0;
    bin[0]=1;for(i=1;i<=n;++i)bin[i]=bin[i-1]<<1;
    for(i=1;i<=n;++i)scanf("%s",s[i]),len[i]=strlen(s[i]),add(i);
    get_fail();
    for(i=0,f[0][1][0]=1;i<l;++i)
        for(j=0;j<=cnt;++j)
            for(rt=data[j],k=0;k<bin[num];++k)
                if(f[i][j][k])for(u=0;u<26;++u)
                    f[i+1][rt->ch[u]->id][k|rt->ch[u]->st]+=f[i][j][k];
    for(j=1;j<=cnt;++j)ans+=f[l][j][bin[num]-1];
    printf("%lld\n",ans);
    if(ans<=42)
    {
        get_solution();sort(str+1,str+ans+1);
        for(i=1;i<=ans;++i)str[i].print();
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章