洛谷P3401 [USACO12JAN]Video Game G(AC自动机+记忆化搜索)

题目链接:https://www.luogu.com.cn/problem/P3041

无关的话:最近在学AC自动机,感觉很多AC自动机和矩阵快速幂以及dp有关系。那些板子题其实对板子的要求还是很高的,我听说指针版AC自动机会快一些,奈何我不会指针,就自己瞎凑了一个板子出来。以前听学长说有些东西只有0次和N次,我今天第一次接触面向对象保存板子,发现真的好用。以后遇到数据机构我都用面向对象封装模板了。

题解:

  1. 在AC自动机上如果一个点表示一个字符串以及它的后缀,则一个点可以对应多个combo技能(这是关键点)
  2. 可以先用拓扑排序处理出每个点对应了多少个combo技能,处理时要用fail的反向边即refail计算入度(这里应该不用这么麻烦,build的时候就可以直接加上去了)
  3. 然后就是记忆化搜索啦,这题没啥坑点。
  4. 我的板子tot最后需要减个1(因为实际上是每次我都预处理的)

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=1e9+7;
int n,k;
int indeg[1005];
vector<int> refail[1005];
class ac_auto{
    public:
        const static int maxn=5e4+100;
        int tot,root;
        int tree[maxn][5]; 
        int num[maxn];
        int fail[maxn];
        int newnode(){
            for(int i=0;i<3;i++){
                tree[tot][i]=0;
            }
            num[tot]=0;
            return tot++;
        }
        
        void init(){
            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];
            }
            num[now]++;
        }
        
        void getFail(){
            queue <int>q;
            for(int i=0;i<3;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<3;i++){
                    if(tree[now][i]){
                        fail[tree[now][i]] = tree[fail[now]][i];
                        refail[tree[fail[now]][i]].pb(tree[now][i]);
                        indeg[tree[now][i]]++;
                        q.push(tree[now][i]);
                    }
                    else
                        tree[now][i] = tree[fail[now]][i];
                }
            }
        }    
        
}acam;
void toposort(){
    queue<int> q;
    for(int i=0;i<acam.tot;i++){
        if(!indeg[i]) q.push(i);
    }
    while(!q.empty()){
        int now=q.front();q.pop();
        for(auto v:refail[now]){
            acam.num[v]+=acam.num[now];
            if(--indeg[v]==0) q.push(v);
        }
    }
}
int dp[1010][410];
int dfs(int pos,int now){
    if(pos>k) return 0;
    if(dp[pos][now]) return dp[pos][now];
    int ans=0;
    rep(i,0,2){
        ans=max(ans,acam.num[acam.tree[now][i]]+dfs(pos+1,acam.tree[now][i]));
    }
    return dp[pos][now]=ans;
}
int main(){
    cin>>n>>k;
    acam.init();
    rep(i,1,n){
        string s;cin>>s;
        acam.insert_(s);
    }
    acam.getFail();
    toposort();
    cout<<dfs(1,0)<<endl;
}
View Code
原文地址:https://www.cnblogs.com/Anonytt/p/13434009.html