HDU-2296 Ring(AC自动机+DP)
题目大意:给出的m个字符串都有一个权值。用小写字母构造一个长度不超过n的字符串S,如果S包含子串s,则S获取s的权值。输出具有最大权值的最小字符串S。
题目分析:先建立AC自动机。定义状态dp(step,u)表示长度为step、在u节点上的最大权值。状态转移方程为:dp(step,u)=max(dp(step-1,v)+w(u))。其中,v为能到达u的前一个节点。
代码如下:
# include<iostream> # include<cstdio> # include<queue> # include<string> # include<cstring> # include<algorithm> using namespace std; int cnt; int ch[1200][26]; int fail[1200]; int val[1200]; int w[105]; void init() { cnt=0; memset(ch,-1,sizeof(ch)); memset(val,0,sizeof(val)); } int idx(char c) { return c-'a'; } void insert(char *s,int key) { int len=strlen(s); int r=0; for(int i=0;i<len;++i){ int c=idx(s[i]); if(ch[r][c]==-1) ch[r][c]=++cnt; r=ch[r][c]; } val[r]=w[key]; } void getFail() { queue<int>q; fail[0]=0; for(int i=0;i<26;++i){ if(ch[0][i]==-1) ch[0][i]=0; else{ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;++i){ if(ch[u][i]==-1) ch[u][i]=ch[fail[u]][i]; else{ fail[ch[u][i]]=ch[fail[u]][i]; q.push(ch[u][i]); } } } } char h[105][15]; int dp[55][1200]; string path[55][1200]; bool isSmall(string s,string t) { if(t=="") return true; if(s.size()<t.size()) return true; if(s.size()>t.size()) return false; return s<t; } string DP(int n) { memset(dp,-1,sizeof(dp)); dp[0][0]=0; for(int i=0;i<=n;++i) for(int j=0;j<=cnt;++j) path[i][j]=""; int fen=0; for(int i=0;i<=n;++i){ for(int j=0;j<=cnt;++j){ if(dp[i][j]==-1) continue; for(int c=0;c<26;++c){ if(dp[i+1][ch[j][c]]<dp[i][j]+val[ch[j][c]]){ dp[i+1][ch[j][c]]=dp[i][j]+val[ch[j][c]]; path[i+1][ch[j][c]]=path[i][j]+(char)('a'+c); }else if(dp[i+1][ch[j][c]]==dp[i][j]+val[ch[j][c]]){ if(isSmall(path[i][j]+(char)('a'+c),path[i+1][ch[j][c]])) path[i+1][ch[j][c]]=path[i][j]+(char)('a'+c); } } if(i>0) fen=max(fen,dp[i][j]); } } if(fen==0) return ""; string res=""; for(int i=1;i<=n;++i) for(int j=0;j<=cnt;++j){ if(dp[i][j]==fen&&isSmall(path[i][j],res)){ res=path[i][j]; } } return res; } int main() { int T,n,m; scanf("%d",&T); while(T--) { init(); scanf("%d%d",&n,&m); for(int i=0;i<m;++i) scanf("%s",h[i]); w[0]=0; for(int i=1;i<=m;++i) scanf("%d",w+i); for(int i=0;i<m;++i) insert(h[i],i+1); getFail(); cout<<DP(n)<<endl; } return 0; }