POJ 1625(AC自动机+dp)
传送门
题面:
Censored!
Time Limit: 5000MS | Memory Limit: 10000K | |
Total Submissions: 11233 | Accepted: 3069 |
Description
The alphabet of Freeland consists of exactly N letters. Each sentence of Freeland language (also known as Freish) consists of exactly M letters without word breaks. So, there exist exactly N^M different Freish sentences.
But after recent election of Mr. Grass Jr. as Freeland president some words offending him were declared unprintable and all sentences containing at least one of them were forbidden. The sentence S contains a word W if W is a substring of S i.e. exists such k >= 1 that S[k] = W[1], S[k+1] = W[2], ...,S[k+len(W)-1] = W[len(W)], where k+len(W)-1 <= M and len(W) denotes length of W. Everyone who uses a forbidden sentence is to be put to jail for 10 years.
Find out how many different sentences can be used now by freelanders without risk to be put to jail for using it.
Input
The first line of the input file contains three integer numbers: N -- the number of letters in Freish alphabet, M -- the length of all Freish sentences and P -- the number of forbidden words (1 <= N <= 50, 1 <= M <= 50, 0 <= P <= 10).
The second line contains exactly N different characters -- the letters of the Freish alphabet (all with ASCII code greater than 32).
The following P lines contain forbidden words, each not longer than min(M, 10) characters, all containing only letters of Freish alphabet.
Output
Output the only integer number -- the number of different sentences freelanders can safely use.
Sample Input
2 3 1 ab bb
Sample Output
5
题意:
有一个大小为n的字母表,你可以用这n种字母去造单词。现在有给你p个单词,让你造出一个长度为m的单词,使得你造出的单词不含有给定的p个单词。问你可以造出的方案数为多少。
题目分析:
类似bzoj1030,我们可以直接用dp去解决。
我们设dp[i][j]为当前长度为i的字符串处于Trie树中的第j号结点所具有的方案数。
根据AC自动机next数组的性质,我们不难发现,长度为i+1,位于第j个结点的方案数可以转移到长度为i,位于第next[j][k]结点的方案数,即dp[i+1][j]=dp[i+1][j]+dp[i][next[j][k]]。而当我们遍历到第j 个结点时发现它可以作为终止结点,我们就跳过该结点,此时就可以求出不符合条件(凑不出完整字符串)的方案数了。
另外,这个题中没有对答案进行取模,因此还得需要贴上大数模板。
代码:
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <cstring>
#include <iostream>
#define maxn 107
using namespace std;
map<char,int>mp;
char st[maxn];
char table[maxn];
struct Trie{//AC自动机
int next[maxn][60],fail[maxn],root,id,End[maxn],n;
int newnode(){
for(int i=0;i<51;i++){
next[id][i]=-1;
}
End[id]=0;
//cout<<id<<endl;
return id++;
}
void init(int _n,char *str){
id=0;
n=_n;
root=newnode();
int len=strlen(str);
mp.clear();
for(int i=0;i<len;i++){
mp[str[i]]=i;
}
}
void Insert(char *str){
int len=strlen(str);
int now=root;
for(int i=0;i<len;i++){
if(next[now][mp[str[i]]]==-1){
next[now][mp[str[i]]]=newnode();
//cout<<id<<endl;
}
now=next[now][mp[str[i]]];
}
End[now]=1;
//cout<<id<<endl;
}
void build(){
queue<int>que;
for(int i=0;i<n;i++){
if(next[root][i]==-1){
next[root][i]=root;
}
else{
fail[next[root][i]]=root;
que.push(next[root][i]);
}
}
while(!que.empty()){
int now=que.front();
que.pop();
for(int i=0;i<n;i++){
if(next[now][i]==-1){
next[now][i]=next[fail[now]][i];
}
else{
fail[next[now][i]]=next[fail[now]][i];
que.push(next[now][i]);
}
}
End[now]|=End[fail[now]];
}
//cout<<id<<endl;
}
}ac;
struct BigInteger{//大整数模板
int A[25];
enum{MOD = 10000};
BigInteger(){memset(A, 0, sizeof(A)); A[0]=1;}
void set(int x){memset(A, 0, sizeof(A)); A[0]=1; A[1]=x;}
void print(){
printf("%d", A[A[0]]);
for (int i=A[0]-1; i>0; i--){
if (A[i]==0){printf("0000"); continue;}
for (int k=10; k*A[i]<MOD; k*=10) printf("0");
printf("%d", A[i]);
}
printf("\n");
}
int& operator [] (int p) {return A[p];}
const int& operator [] (int p) const {return A[p];}
BigInteger operator + (const BigInteger& B){
BigInteger C;
C[0]=max(A[0], B[0]);
for (int i=1; i<=C[0]; i++)
C[i]+=A[i]+B[i], C[i+1]+=C[i]/MOD, C[i]%=MOD;
if (C[C[0]+1] > 0) C[0]++;
return C;
}
BigInteger operator * (const BigInteger& B){
BigInteger C;
C[0]=A[0]+B[0];
for (int i=1; i<=A[0]; i++)
for (int j=1; j<=B[0]; j++){
C[i+j-1]+=A[i]*B[j], C[i+j]+=C[i+j-1]/MOD, C[i+j-1]%=MOD;
}
if (C[C[0]] == 0) C[0]--;
return C;
}
};
BigInteger dp[60][maxn];
int main()
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
scanf("%s",table);
ac.init(n,table);
for(int i=0;i<k;i++){
scanf("%s",st);
ac.Insert(st);
}
ac.build();
//cout<<ac.id<<endl;
dp[0][0].set(1);
for(int i=1;i<=m;i++){//dp转移
for(int j=0;j<ac.id;j++){
for(int k=0;k<n;k++){
if(ac.End[ac.next[j][k]]) continue;
dp[i][ac.next[j][k]]=dp[i-1][j]+dp[i][ac.next[j][k]];
}
}
}
BigInteger res;
for(int i=0;i<ac.id;i++){
res=res+dp[m][i];
}
res.print();
return 0;
}