K 破忒头的匿名信(ac自动机+小dp)

题:https://ac.nowcoder.com/acm/contest/4010/K

题意:用一些模式串凑成一个目标串,每个模式串有消耗,问组合的最小消耗,或不能组成输出-1;

分析:典型的AC自动机处理后在跳fail的过程中进行操作,这里操作就是dp计算最小。用dp[i]表示长串前ii位的最小代价,若有一个单词s是长串的前ii项的后缀,那么可以用dp[i−len(s)]+val(s)转移到dp[i]

#include
using namespace std;
typedef long long ll;
const ll INF=1e18;
const int M=5e5+6;
int trie[M][26],fail[M],cnt;
ll val[M],dp[M],deep[M];
void insert(string s,ll v){
    int root=0,len=s.size();
    for(int i=0;i){
        if(!trie[root][s[i]-'a'])
            trie[root][s[i]-'a']=++cnt;
        root=trie[root][s[i]-'a'];
    }
    val[root]=min(val[root],v);
    deep[root]=len;
}
void getfail(){
    queue<int>que;
    while(!que.empty())
        que.pop();
    for(int i=0;i<26;i++)
        if(trie[0][i]){
            fail[trie[0][i]]=0;
            que.push(trie[0][i]);
        }
    while(!que.empty()){
        int now=que.front();
        que.pop();
        for(int i=0;i<26;i++){
            if(trie[now][i]){
                fail[trie[now][i]]=trie[fail[now]][i];
                que.push(trie[now][i]);
            }
            else
                trie[now][i]=trie[fail[now]][i];
        }
    }
}
ll query(string s){
    int now=0,len=s.size();
    for(int i=0;i){
        now=trie[now][s[i]-'a'];
        for(int j=now;j;j=fail[j]){
            if(deep[j])
                dp[i+1]=min(dp[i+1],dp[i+1-deep[j]]+val[j]);
        }
    }
    if(dp[len]>=INF)
        return -1;
    return dp[len];
}
void init(){
    for(int i=0;i)
        dp[i]=val[i]=INF;
    dp[0]=val[0]=0;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    init();
    int n;
    string s;
    cin>>n;
    for(int i=1;i<=n;i++){
        ll v;
        cin>>s>>v;
        insert(s,v);
    }
    fail[0]=0;
    getfail();
    cin>>s;
    cout<<query(s);
    return 0;
}
View Code

 

你可能感兴趣的:(K 破忒头的匿名信(ac自动机+小dp))