题意:
一个神奇的国度他们字母类型有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 */