2020 CCPC Wannafly Winter Camp Day2 K题 破忒头的匿名信(AC自动机+dp)
这题要看出dp更新的方法,因为我们有很多字符串,而最后的串可以随意组装而来
因此dp状态设计为f[i]到i的最小代价,这也一直往下跳,更新的时候更新fail链上的所有答案,因为这些是可以成为他的后缀更新过来的,这也就枚举到了所有的情况
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=5e5+10; struct node{ int cnt; node * nxt[27]; node * fail; vector<node *> num; }*rt; node pool[N]; int n,m,idx; ll val[N]; int num; int st[N]; ll f[N]; void insert(string s,int x){ node *p=rt; int i; for(i=0;i<s.size();i++){ int sign=s[i]-'a'; if(p->nxt[sign]==NULL){ p->nxt[sign]=pool+(++idx); p->nxt[sign]->cnt=++num; } p=p->nxt[sign]; if(i==(int)s.size()-1){ if(!val[p->cnt]) val[p->cnt]=x; else val[p->cnt]=min(val[p->cnt],1ll*x); st[p->cnt]=(int)s.size(); } } } void build(){ int i; queue<node *> q; rt->fail=rt; for(i=0;i<26;i++){ if(rt->nxt[i]){ rt->nxt[i]->fail=rt; rt->num.push_back(rt->nxt[i]); q.push(rt->nxt[i]); } else{ rt->nxt[i]=rt; rt->nxt[i]->fail=rt; } } while(q.size()){ auto t=q.front(); q.pop(); for(i=0;i<26;i++){ if(t->nxt[i]){ t->nxt[i]->fail=t->fail->nxt[i]; t->fail->nxt[i]->num.push_back(t->nxt[i]); q.push(t->nxt[i]); } else{ t->nxt[i]=t->fail->nxt[i]; } } } } void dfs(string s){ int len=(int)s.size(); int i,j; node *p=rt; for(i=0;i<len;i++){ p=p->nxt[s[i]-'a']; node *x=p; while(x!=rt){ if(st[x->cnt]){ f[i+1]=min(f[i+1],f[i+1-st[x->cnt]]+val[x->cnt]); } x=x->fail; } } if(f[len]==1e18){ cout<<-1<<endl; } else{ cout<<f[len]<<endl; } } int main(){ ios::sync_with_stdio(false); rt=pool; rt->cnt=0; int i,j; cin>>n; for(i=1;i<=n;i++){ string s; int x; cin>>s>>x; insert(s,x); } build(); string s; cin>>s; for(int i=0;i<=(int)s.size();i++) f[i]=1e18; f[0]=0; dfs(s); }
没有人不辛苦,只有人不喊疼