星夜行 2017-10-11
题面链接
首先构建出AC自动机
然后在AC自动机上面跑DP
转移很显然从Trie树的节点跳到他的儿子节点
但是要注意一个问题,
在计算的时候,每一个节点加入后能够
造成的贡献
要加上他的子串的贡献
至于DP:
设f[i][j]表示已经使用了i个字母
当前在Trie树的第j个节点上面能够产生的最大贡献
很显然,转移到他的儿子节点上面,同时统计贡献即可
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<queue> #include<algorithm> using namespace std; #define INF 1000000 int N,K; int tot=0,ans; char s[20]; int f[1100][400]; struct Node { int vis[3]; int p; int fail; }t[5000]; void Add(char *s) { int l=strlen(s); int now=0; for(int i=0;i<l;++i) { if(!t[now].vis[s[i]-'A']) t[now].vis[s[i]-'A']=++tot; now=t[now].vis[s[i]-'A']; } t[now].p++; } void Build() { queue<int> Q; for(int i=0;i<3;++i) if(t[0].vis[i])Q.push(t[0].vis[i]); while(!Q.empty()) { int u=Q.front();Q.pop(); for(int i=0;i<3;++i) { if(t[u].vis[i]) { t[t[u].vis[i]].fail=t[t[u].fail].vis[i]; Q.push(t[u].vis[i]); } else t[u].vis[i]=t[t[u].fail].vis[i]; } t[u].p+=t[t[u].fail].p; } } void DP() { for(int T=0;T<=K;++T) for(int i=1;i<=tot;++i) f[T][i]=-INF; for(int T=1;T<=K;++T) for(int i=0;i<=tot;++i) for(int j=0;j<3;++j) f[T][t[i].vis[j]]=max(f[T][t[i].vis[j]],f[T-1][i]+t[t[i].vis[j]].p); for(int i=0;i<=tot;++i)ans=max(ans,f[K][i]); } int main() { scanf("%d%d",&N,&K); for(int i=1;i<=N;++i) { scanf("%s",s); Add(s); } Build(); DP(); printf("%d\n",ans); return 0; }