题目链接:https://www.luogu.com.cn/problem/P4052
前言:
学长说AC自动机的DP都非常套路
大部分dp[i][j]表示当前在节点j,且串长为i时的情况,
有时再加一维表示这个状态里面包含了哪些东西
而且AC自动机的DP会经常让你用矩阵乘法优化
题解:
我个人想说的:也是第一次做AC自动机上的DP问题,一开始那里End为什么在转移的时候需要 |= 呢?因为有可能一个串的结尾原先insert进去后end为1,但是它的fail却是0,这个时候原先的1就会被0覆盖掉,所以用|=来维护当前的End。
AC代码:
#include<bits/stdc++.h> #pragma GCC optimize(2) #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl ' ' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e4+7; class ac_auto{ public: const static int maxn=5e4+100; int tot,root; int tree[maxn][30]; int End[maxn]; int fail[maxn];int ans[1005]; int newnode(){ for(int i=0;i<26;i++){ tree[tot][i]=0; } End[tot]=0; return tot++; } void init(){ mem(ans,0);tot=0; root=newnode(); } void insert_(string s){ int now = root; for(int i=0;i<s.length();i++){ int id=s[i]-'A'; if(!tree[now][id]){ tree[now][id]=newnode(); } now=tree[now][id]; } End[now]|=1; } void getFail(){ queue <int>q; for(int i=0;i<26;i++){ if(tree[0][i]){ fail[tree[0][i]] = 0; q.push(tree[0][i]); } } while(!q.empty()){ int now = q.front(); q.pop(); for(int i=0;i<26;i++){ if(tree[now][i]){ fail[tree[now][i]] = tree[fail[now]][i]; End[tree[now][i]] |= End[tree[fail[now]][i]]; q.push(tree[now][i]); } else tree[now][i] = tree[fail[now]][i]; } } } }acam; int dp[105][10005]; ll qp(ll a,ll b,ll c){ ll ans = 1; ll base = a%c; while(b){ if(b & 1) ans = (ans*base)%c; base = (base*base)%c; b >>= 1; } return ans; } int main(){ int n,m;scanf("%d%d",&n,&m); acam.init(); rep(i,1,n){ string s;cin>>s; acam.insert_(s); } acam.getFail(); dp[0][0]=1; rep(i,1,m){ rep(j,0,acam.tot-1){ rep(k,0,25){ if(!acam.End[acam.tree[j][k]]){ dp[i][acam.tree[j][k]]=(dp[i][acam.tree[j][k]]+dp[i-1][j])%mod; } } } } int ans=0; rep(i,0,acam.tot-1){ ans=(ans+dp[m][i])%mod; } ans=(qp(26,m,mod)-ans+mod)%mod; cout<<ans<<endl; }