BZOJ 1030 AC自动机+DP

询问由26个字母组成的长度为n的字符串中含有给定一些字符串为子串的种数。

考虑补集。我们求出不含有给定字符串为子串的种数。

这个问题可以由AC自动机上DP求得。

对给定的串建立AC自动机。我们令dp[i][j]表示字符串第i位走到AC自动机上j节点的方法数。那么对应于一个字符串,就相当于AC自动机走到了某个节点的位置。

现在由于不能包含某些字符串为子串,那么这个子串的end节点一定不能走,fail指针指向这些子串的end节点的节点也不能走。

剩下的就可以进行DP了。那么dp[i][ch[j][k]]+=dp[i-1][j].

 

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi 3.1415926535
# define eps 1e-4
# define MOD 10007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline int Scan() {
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}
const int N=6010;
//Code begin...

int trie[N][27], top, fail[N], dp[105][N];

void init(){top=1; mem(trie[0],0);}
void ins(char *s){
    int rt, nxt;
    for (rt=0; *s; rt=nxt, ++s){
        nxt=trie[rt][*s-'A'];
        if (!nxt) mem(trie[top],0), trie[rt][*s-'A']=nxt=top++;
    }
    trie[rt][26]=1;
}
void makefail(){
    int u, v, bg, ed;
    static int q[N];
    fail[0]=bg=ed=0;
    FO(i,0,26) if ((v=trie[0][i])) fail[q[ed++]=v]=0;
    while (bg<ed){
        u=q[bg++];
        FO(i,0,26) {
            if ((v=trie[u][i])) fail[q[ed++]=v]=trie[fail[u]][i];
            else trie[u][i]=trie[fail[u]][i];
        }
        trie[u][26]|=trie[fail[u]][26];
    }
}
int work(int m){
    int ans1=1, ans2=0;
    FOR(i,1,m) ans1=ans1*26%MOD;
    dp[0][0]=1;
    FOR(i,1,m) FO(j,0,top) {
        if (trie[j][26]) continue;
        FO(k,0,26) dp[i][trie[j][k]]=(dp[i][trie[j][k]]+dp[i-1][j])%MOD;
    }
    FO(i,0,top) if (!trie[i][26]) ans2=(ans2+dp[m][i])%MOD;
    return ((ans1-ans2)%MOD+MOD)%MOD;
}
char str[105];
int main ()
{
    int n, m;
    scanf("%d%d",&n,&m); init();
    FOR(i,1,n) scanf("%s",str), ins(str);
    makefail();
    printf("%d\n",work(m));
    return 0;
}
View Code

 

posted @ 2017-04-13 15:38  free-loop  阅读(94)  评论(0编辑  收藏