HDU 2296 Ring (AC自动机+DP)
Ring
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3966 Accepted Submission(s): 1316
Problem Description
For the hope of a forever love, Steven is planning to send a ring to Jane with a romantic string engraved on. The string's length should not exceed N. The careful Steven knows Jane so deeply that he knows her favorite words, such as "love", "forever". Also, he knows the value of each word. The higher value a word has the more joy Jane will get when see it.
The weight of a word is defined as its appeared times in the romantic string multiply by its value, while the weight of the romantic string is defined as the sum of all words' weight. You should output the string making its weight maximal.
The weight of a word is defined as its appeared times in the romantic string multiply by its value, while the weight of the romantic string is defined as the sum of all words' weight. You should output the string making its weight maximal.
Input
The input consists of several test cases. The first line of input consists of an integer T, indicating the number of test cases. Each test case starts with a line consisting of two integers: N, M, indicating the string's length and the number of Jane's favorite words. Each of the following M lines consists of a favorite word Si. The last line of each test case consists of M integers, while the i-th number indicates the value of Si.
Technical Specification
1. T ≤ 15
2. 0 < N ≤ 50, 0 < M ≤ 100.
3. The length of each word is less than 11 and bigger than 0.
4. 1 ≤ Hi ≤ 100.
5. All the words in the input are different.
6. All the words just consist of 'a' - 'z'.
Technical Specification
1. T ≤ 15
2. 0 < N ≤ 50, 0 < M ≤ 100.
3. The length of each word is less than 11 and bigger than 0.
4. 1 ≤ Hi ≤ 100.
5. All the words in the input are different.
6. All the words just consist of 'a' - 'z'.
Output
For each test case, output the string to engrave on a single line.
If there's more than one possible answer, first output the shortest one. If there are still multiple solutions, output the smallest in lexicographically order.
The answer may be an empty string.
If there's more than one possible answer, first output the shortest one. If there are still multiple solutions, output the smallest in lexicographically order.
The answer may be an empty string.
Sample Input
2
7 2
love
ever
5 5
5 1
ab
5
Sample Output
lovever
abab
分析: 枚举构造的所有的可能性,用DP记录最大值
这里 AC自动机中end数组记录在该节点的价值.
并且如果我们走到这个节点的话,就会获得这个节点的价值,
那么就会想到我们也能得到是它的前缀的节点的价值,所以end节点记录该节点和前缀节点的价值之和,
用fail数组的性质我们就能实现这个.
字典序要求最小,我们在DP的时候和最后找结果的时候,都进行一下字符串大小的比较就可以了
代码如下:
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> #include <string> using namespace std; #define INF 0x3f3f3f3f struct node { int x; string a; }dp[55][1500]; int maxx; string maxstr; string b; char str[110][15]; int val[110]; struct Trie { int Next[1500][26];//26是这里讨论26个小写字母的情况,根据情况修改 int fail[1500],end[1500];//end数组表示以该节点结尾的字符串的数量 int root,L;//L用来标记节点序号,以广度优先展开的字典树的序号 int newnode() //建立新节点 { for(int i = 0;i < 26;i++) Next[L][i] = -1; //将该节点的后继节点域初始化 end[L++] = 0; return L-1; //返回当前节点编号 } void init() //初始化操作 { L = 0; root = newnode(); } void insert(char buf[],int val) { int len = strlen(buf); int now = root; for(int i = 0;i < len;i++) { if(Next[now][buf[i]-'a'] == -1) //如果未建立当前的后继节点,建立新的节点 Next[now][buf[i]-'a'] = newnode(); now = Next[now][buf[i]-'a']; } end[now]+=val;//以该节点结尾的字符串数量增加1 } void build() { queue<int>Q; //用广度优先的方式,将树层层展开 fail[root] = root; for(int i = 0;i < 26;i++) if(Next[root][i] == -1) Next[root][i] = root; else { fail[Next[root][i]] = root; Q.push(Next[root][i]); } while( !Q.empty() ) { int now = Q.front(); Q.pop(); end[now]+=end[fail[now]]; for(int i = 0;i < 26;i++) if(Next[now][i] == -1) Next[now][i] = Next[fail[now]][i];//该段的最后一个节点匹配后,跳到拥有最大公共后缀的fail节点继续匹配 else { fail[Next[now][i]]=Next[fail[now]][i];//当前节点的fail节点等于它前驱节点的fail节点的后继节点 Q.push(Next[now][i]); } } } int query(char buf[]) { int len = strlen(buf); int now = root; int res = 0; for(int i = 0;i < len;i++) { now = Next[now][buf[i]-'a']; int temp = now; while( temp != root ) { res += end[temp];//加上以当前节点结尾的字符串数 end[temp] = 0;//该题是防止计算重复的字符串 temp = fail[temp];//每次找最大公共后缀对应的fail节点 } } return res; } void solve(int len) { for(int i=0;i<=len;i++) for(int j=0;j<L;j++){ dp[i][j].x=INF; dp[i][j].a.clear(); } dp[0][0].x=0; for(int i=0;i<len;i++) for(int j=0;j<L;j++) { if(dp[i][j].x<INF) { for(int k=0;k<26;k++) { int news=Next[j][k]; if(dp[i+1][news].x==INF||dp[i+1][news].x<dp[i][j].x+end[news]) { dp[i+1][news].x=dp[i][j].x+end[news]; dp[i+1][news].a=dp[i][j].a+(char)('a'+k); } else if(dp[i+1][news].x==dp[i][j].x+end[news]&&((dp[i+1][news].a.size()>dp[i][j].a.size()+1)||((dp[i+1][news].a.size()==dp[i][j].a.size()+1)&&dp[i+1][news].a>(dp[i][j].a+(char)('a'+k))))) { dp[i+1][news].x=dp[i][j].x+end[news]; dp[i+1][news].a=dp[i][j].a+(char)('a'+k); } } } } maxx=0; maxstr=""; for(int i=1;i<=len;i++) for(int j=0;j<L;j++) { if(dp[i][j].x<INF){ if((maxx<dp[i][j].x)||(maxx==dp[i][j].x&&dp[i][j].a.size()<maxstr.size())||(maxx==dp[i][j].x&&dp[i][j].a.size()==maxstr.size()&&dp[i][j].a<maxstr)){ maxx=dp[i][j].x; maxstr=dp[i][j].a; } } } } void debug() { for(int i = 0;i < L;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j = 0;j < 26;j++) printf("%2d",Next[i][j]); printf("\n"); } } }; char buf[1500]; Trie ac; int main() { int t,n,ans,m; scanf("%d",&t); while(t--) { ac.init(); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%s",str[i]); for(int i=0;i<m;i++){ scanf("%d",&val[i]); ac.insert(str[i],val[i]); } ac.build(); ac.solve(n); cout<<maxstr<<endl; } return 0; }