bzoj1030: [JSOI2007]文本生成器(AC自動機+DP)

  第一次寫這類題...懵ios

  直接計算答案很差計算,因此補集轉化求不合法的方案。ide

  首先考慮樸素的DP,設$f(i, s)$表示前$i$個字符,字符串爲$s$的方案數,且任意一個給定串都不存在$s$中。spa

  咱們知道在一個字符串裏找其餘的字符串是AC自動機的強項,那麼咱們就能夠考慮在AC自動機上跑DP,每次$+j$都在AC自動機上匹配,若是匹配到單詞結尾的話就不能轉移,不然就是能夠轉移的。code

  因此設$f(i, j)$爲前$i$個字符,當前匹配到AC自動機上第$j$個節點的方案數,若是沿着fail一直往上的全部節點都不是單詞結尾就能夠轉移了。blog

  注意是大寫字母T_T字符串

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define MOD(x) ((x)>=mod?(x)-mod:(x))
#define ll long long
using namespace std;
const int maxn=6010, maxm=110, mod=10007;
struct poi{int nxt[26], fail;}tree[maxn*maxm];
int n, m, ans, tott;
int f[maxm][maxn], h[maxn];
bool cnt[maxn];
char s[maxm];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<'0' || c>'9') c=='-'&&(f=-1), c=getchar();
    while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
    k*=f;    
} 
inline void insert()
{
    int len=strlen(s+1), now=0;
    for(int i=1, ch;i<=len;i++)
    {
        if(!tree[now].nxt[ch=s[i]-'A']) 
        tree[now].nxt[ch]=++tott;
        now=tree[now].nxt[ch];
    }
    cnt[now]=1;
}
inline void getfail()
{
    int front=1, rear=0; tree[0].fail=-1;
    for(int i=0, too;i<26;i++) 
    if((too=tree[0].nxt[i])) h[++rear]=too;
    while(front<=rear)
    {
        int now=h[front++];
        for(int i=0, too;i<26;i++)
        if((too=tree[now].nxt[i])) 
        tree[too].fail=tree[tree[now].fail].nxt[i], h[++rear]=too;
        else tree[now].nxt[i]=tree[tree[now].fail].nxt[i];
        cnt[now]|=cnt[tree[now].fail];
    }
}
int main()
{
    read(n); read(m);
    for(int i=1;i<=n;i++) scanf("%s", s+1), insert();
    getfail(); f[0][0]=1;
    for(int i=1;i<=m;i++)
    for(int j=0;j<=tott;j++)
    if(!cnt[j]) for(int k=0;k<26;k++)
    f[i][tree[j].nxt[k]]=MOD(f[i][tree[j].nxt[k]]+f[i-1][j]);
    for(int i=0;i<=tott;i++) if(!cnt[i]) ans=MOD(ans+f[m][i]);
    int tot=1; for(int i=1;i<=m;i++) tot=1ll*tot*26%mod;
    printf("%d\n", MOD(tot-ans+mod));
}
View Code
相關文章
相關標籤/搜索