BZOJ1030 JSOI2007 文本生成器 题解&代码

题意:给出n个匹配串,询问:对于长度为m的串,有多少个串至少包含一个匹配串(答案对10007取模)
题解:
“至少包含一个匹配串的长度为m的串”,那么很容易转化为“所有串除去不包含任何匹配串的长度为m的串”
然后就是喜闻乐见的AC自动机上的dp了,dp方程显然是dp[i][j]表示长度为i的串匹配到j位时有多少不包含任何匹配串
有:dp[i][ch[j][k]]+=dp[i-1][j]
即孩子节点一定由有效父亲节点推知(节点本身flag指针就是true的当然是0)

/**************************************************************
 Problem: 1030
 User: Rainbow6174
 Language: C++
 Result: Accepted
 Time:76 ms
 Memory:4396 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#define MOD 10007
using namespace std;
const int maxt = 6005;
int n, m, tot, ans = 1, temp, ch[maxt][26], fail[maxt], q[maxt], dp[105][maxt];
char s[105];
bool flag[maxt];
void newnode(int x, int v)
{
 ch[x][v]=++tot;
}
void addtrie(char s[])
{
 int x = 0, p = 0, len = strlen(s);
 while( p < len )
 {
 temp = s[p]-'A';
 if ( !ch[x][temp] ) newnode(x, temp);
 x = ch[x][temp];
 p++;
 }
 flag[x] = 1;
}
void AC(void)
{
 int h = 0, t = 0;
 for(int i = 0; i < 26; i++)
 if(ch[0][i]) q[t++] = ch[0][i];
 while( h < t )
 {
 temp = q[h++];
 for(int i = 0; i < 26; i++)
 if( !ch[temp][i] ) ch[temp][i] = ch[fail[temp]][i];
 else fail[ch[temp][i]] = ch[fail[temp]][i],
 flag[ch[temp][i]] |= flag[fail[ch[temp][i]]],
 q[t++] = ch[temp][i];
 }
}
int main(void)
{
 scanf("%d%d", &n, &m);
 for(int i = 0; i < n; i++)
 {
 scanf("%s", s);
 addtrie(s);
 }
 AC();
 dp[0][0] = 1;
 for(int i = 1; i <= m; i++)
 {
 for(int j = 0; j <= tot; j++)
 {
 if(flag[j] || !dp[i-1][j]) continue;
 for(int k = 0; k < 26; k++)
 {
 dp[i][ch[j][k]] += dp[i-1][j];
 dp[i][ch[j][k]] %= MOD;
 }
 }
 ans*=26;
 ans%=MOD;
 }
 //cout<<ans<<endl;
 for(int i = 0; i <= tot; i++)
 {
 //cout<<i<<' '<<flag[i]<<endl;
 if(!flag[i])
 {
 //cout<<dp[m][i]<<endl;
 ans -= dp[m][i];
 ans += MOD;
 ans %= MOD;
 }
 }
 printf("%d\n",ans);
 return 0;
}

你可能感兴趣的:(dp,bzoj)