题意:
一个神奇的国度他们字母类型有n个,并且每句话必须是m个字母组成的,但是国王很奇葩,他设定了一些单词,只要句子中出现这样的单词那么造句的人就好入狱。给出了p个不能出现的单词。问有多少种句子可以出现。
题解:
ac自动上的dp,第一次做参照了bin神的代码,很神奇啊,ac自动机上也可以dp 0 0!
好了我以自己理解来分析这题。
首先,我们要将所给的n个字符进行处理,因为这n个字符有可能不是以阿斯科马值相邻的,那么我们用map离散化。
接着,我们将限制的串插入自动机中,凡是出现end[i]==0的就是可行的串。
然后,建ac自动机时,要注意一点,如果适配指针对应的节点是限制串(fail[i]==1)那么对应的这个指向适配指正的对应节点也应该是限制串,这个要好好理解下。
最后我们只要这样设置状态dp[step][i]走了step步,并且以i节点为结尾的合法句子个数。只要枚举上一步的节点是哪个即可。注意要排出限制的:if(end[i]==1)continue;
最后一点,答案太大,要用大数存。我懒得写,可耻的盗用了别人的大数类。
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=10007;
const int maxn=1005;
const int SIZE=110;
char buff[maxn];
map<char,int>mat;
struct AC
{
int next[SIZE][256],fail[SIZE],end[SIZE],Q[SIZE*256];
int root,cnt;
void Init()
{
cnt=0;
root=newNode();
}
int newNode()
{
for(int i=0;i<256;i++)
next[cnt][i]=-1;
end[cnt++]=0;
return cnt-1;
}
void Insert(char buff[])
{
int now=root;
int len=strlen(buff);
for(int i=0,k;i<len;i++)
{
k=mat[buff[i]];
if(next[now][k]==-1)
next[now][k]=newNode();
now=next[now][k];
}
end[now]=1;///这个单词是否存在
}
void build()
{
fail[root]=root;
int front,rear;
front=rear=0;
int now=root;
for(int i=0;i<256;i++)
{
if(next[now][i]==-1)
next[now][i]=root;
else
{
fail[next[now][i]]=root;
Q[rear++]=next[now][i];
}
}
while(front<rear)
{
now=Q[front++];
if(end[fail[now]]) end[now]=1;///这句话非常重要,没加wa一发
for(int i=0;i<256;i++)
{
if(next[now][i]==-1)
next[now][i]=next[fail[now]][i];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q[rear++]=next[now][i];
}
}
}
}
/*
void Search(char buff[])
{
int now=root;
int len=strlen(buff);
for(int i=0;i<len;i++)
{
now=next[now][buff[i]];
int temp=now;
while(temp!=root)
{
if(end[temp])
StrId.insert(end[temp]);
ans[end[temp]]++;
temp=fail[temp];
}
}
}*/
void Debug()
{
for(int i=0;i<cnt;i++)
{
printf("id=%3d fail=%3d end=%3d child=[",i,fail[i],end[i]);
for(int j=0;j<26;j++)
printf(" %3d",next[i][j]);
printf(" ]\n");
}
}
};
AC ac;
///大数
struct BigInt
{
const static int mod = 10000;
const static int DLEN = 4;
int a[600],len;
BigInt()
{
memset(a,0,sizeof(a));
len = 1;
}
BigInt(int v)
{
memset(a,0,sizeof(a));
len = 0;
do
{
a[len++] = v%mod;
v /= mod;
}while(v);
}
BigInt(const char s[])
{
memset(a,0,sizeof(a));
int L = strlen(s);
len = L/DLEN;
if(L%DLEN)len++;
int index = 0;
for(int i = L-1;i >= 0;i -= DLEN)
{
int t = 0;
int k = i - DLEN + 1;
if(k < 0)k = 0;
for(int j = k;j <= i;j++)
t = t*10 + s[j] - '0';
a[index++] = t;
}
}
BigInt operator +(const BigInt &b)const
{
BigInt res;
res.len = max(len,b.len);
for(int i = 0;i <= res.len;i++)
res.a[i] = 0;
for(int i = 0;i < res.len;i++)
{
res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0);
res.a[i+1] += res.a[i]/mod;
res.a[i] %= mod;
}
if(res.a[res.len] > 0)res.len++;
return res;
}
BigInt operator *(const BigInt &b)const
{
BigInt res;
for(int i = 0; i < len;i++)
{
int up = 0;
for(int j = 0;j < b.len;j++)
{
int temp = a[i]*b.a[j] + res.a[i+j] + up;
res.a[i+j] = temp%mod;
up = temp/mod;
}
if(up != 0)
res.a[i + b.len] = up;
}
res.len = len + b.len;
while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--;
return res;
}
void output()
{
printf("%d",a[len-1]);
for(int i = len-2;i >=0 ;i--)
printf("%04d",a[i]);
printf("\n");
}
};
BigInt dp[2][SIZE];
int main()
{
int n,m,p;
while(scanf("%d %d %d",&n,&m,&p)!=EOF)
{
scanf("%s",buff);
mat.clear();
for(int i=0;buff[i];i++)
mat[buff[i]]=i;
ac.Init();
for(int i=1;i<=p;i++)
{
scanf("%s",buff);
ac.Insert(buff);
}
ac.build();
int now=0,pre=1;
dp[now][0]=1;
for(int i=1;i<ac.cnt;i++)
dp[now][i]=0;
for(int t=1;t<=m;t++)
{
now^=1;
pre^=1;
for(int i=0;i<ac.cnt;i++)
dp[now][i]=0;
for(int i=0;i<ac.cnt;i++)
{
if(ac.end[i])continue;///存在这样的路径跳过.
for(int j=0;j<n;j++)
{
int k=ac.next[i][j];
if(ac.end[k])continue;
dp[now][k]=dp[now][k]+dp[pre][i];
}
}
}
BigInt ans=0;
for(int i=0;i<ac.cnt;i++)
ans=ans+dp[now][i];
ans.output();
}
return 0;
}
/**
5 5 5
abcde
bee
edcba
de
e
bbaed
ans=1024
*/