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<bits/stdc++.h> 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<len;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<len;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<M;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; }