题目大意:给你一个字母表,给定一些敏感字符串,问长度为m且不含任意敏感字符串的串有多少个。(字符全部来自字母表)
思路:首先第一个坑点是输入的字符是unsigned char,可能出现负的ASCII码值,我的解决方法是整体加128。 此外,这题和POJ2778有点类似,不过POJ2778不需要用大整数,用矩阵快速幂可以过,但这题需要用高精度存答案,高精度+矩阵快速幂会tle。
所以我们用动态规划,对建好的状态机进行状态转移(AC自动机建状态图的过程就不赘述了),令dp[i][j]为目前走了i步的情况下,到达j号节点的方案数。初始化dp[0][0]=1
状态转移方程:dp[i][j]=Σdp[i-1][k] (k为j的"父亲"节点)
转移的具体实现并不是枚举j的每个父亲k,这样不现实,而是枚举i-1状态下每个节点,再枚举每个孩子u,以此更新dp[i][u]
代码其实很好理解:
dp[0][0].set(1);//其余格子初始化成0
for(int i=1;i<=m;i++)
{
for(int j=0;j<=tot;j++)//枚举i-1状态下的根节点0~最后一个节点tot
{
for(int k=0;k<n;k++)
{
int u=tree[j][k];//枚举j的每一个孩子u进行更新
if(!isstr[u])
{
dp[i][u]=dp[i][u]+dp[i-1][j];
}
}
}
}
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
#define INF 0x3f3f3f
#define LL unsigned long long
const int maxn=1e2+20;
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;
}
};
char alpha[55];
BigInteger dp[maxn][maxn];//初始化是0
//思路:建好状态机之后,进行dp,dp[i][j]表示从根节点出发走了i步,走到j节点时的方案数,
//dp[i][j]=dp[i-1][k] 其中j是k的父亲节点,转移到i的时候,对每个节点j都更新一下所有的孩子
int book[600];
char s[55];
int n,m,p;
int tree[maxn][55];
int tot;
bool isstr[maxn];
int fail[maxn];
void Insert(char *str)
{
int root=0,len=strlen(str);
for(int i=0;i<len;i++)
{
int id=book[str[i]+128];
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
isstr[root]=true;
}
void getfail()
{
queue<int> q;
for(int i=0;i<n;i++)
{
if(tree[0][i]) q.push(tree[0][i]);
}
while(!q.empty())
{
int v=q.front();
q.pop();
if(isstr[fail[v]]) isstr[v]=true;
for(int i=0;i<n;i++)
{
if(tree[v][i])
{
fail[tree[v][i]]=tree[fail[v]][i];
q.push(tree[v][i]);
}
else tree[v][i]=tree[fail[v]][i];
}
}
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
scanf("%d%d%d",&n,&m,&p);
scanf("%s",alpha);
for(int i=0;i<n;i++)//每个节点有n个孩子位置
{
book[alpha[i]+128]=i;
}
while(p--)
{
scanf("%s",s);
Insert(s);
}
getfail();
dp[0][0].set(1);
for(int i=1;i<=m;i++)
{
for(int j=0;j<=tot;j++)//枚举根节点0~最后一个节点tot
{
for(int k=0;k<n;k++)
{
int u=tree[j][k];//枚举j的每一个孩子u进行更新
if(!isstr[u])
{
dp[i][u]=dp[i][u]+dp[i-1][j];
}
}
}
}
BigInteger sum;
for(int i=0;i<=tot;i++)
{
if(!isstr[i]) sum=sum+dp[m][i];
}
sum.print();
system("pause");
return 0;
}