Ring
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
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.
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
- T ≤ 15
- 0 < N ≤ 50, 0 < M ≤ 100.
- The length of each word is less than 11 and bigger than 0.
- 1 ≤ Hi ≤ 100.
- All the words in the input are different.
- 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.
Sample Input
2
7 2
love
ever
5 5
5 1
ab
5
Sample Output
lovever
abab
Hint
Sample 1: weight(love) = 5, weight(ever) = 5, so weight(lovever) = 5 + 5 = 10
Sample 2: weight(ab) = 2 * 5 = 10, so weight(abab) = 10
中文题意:
求一串长度不大于n的字符串,要求其优先满足价值最大,价值计算为所包含的模板串的价值总和,并且要求字符串最短且为最小字典序。
题解:
ac自动机+dp,因为刚刚做过一道这类题,所以很快就想出来了dp转移方程,貌似ac自动机结合dp大多数都是这种的转移方法,只不过判断条件不同而已。
首先,定义二维数组dp[i][j]表示处理到长度为i且最终位在编号为j的节点上时的最大分值,那么dp的转移方程为:
for (int k=0~26)//伪代码ovo,表示节点j的26个儿子
dp[i+1][tr[j][k]]=max([dp[i+1][tr[j][k]],dp[i][j]+flag[tr[j][k]]);
然而题目并不是这么简单,它丧心病狂的让你输出路径,这样处理起来就十分麻烦,开一个char型的数组path[][][],存dp[i][j]时的最优路径,具体看代码实现。
代码:
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int tr[1101][26],flag[1101],fail[1101],tot;
queue<int>q;
char s[101][15],path[51][1101][51];
int tag[101],dp[51][1101],n,m,T;
void init(int x)
{
for (int i=0;i<26;i++)
tr[x][i]=0;
flag[x]=0;
fail[x]=0;
}
void add(int id)
{
int now=0;
int len=strlen(s[id]);
for (int i=0;i<len;i++)
{
int tmp=s[id][i]-'a';
if (!tr[now][tmp])
{
tot++;
tr[now][tmp]=tot;
init(tot);
}
now=tr[now][tmp];
}
flag[now]+=tag[id];
}
void acatm()
{
for (int i=0;i<26;i++) if (tr[0][i]) q.push(tr[0][i]);
while(!q.empty())
{
int now=q.front();q.pop();
for (int i=0;i<26;i++)
if (tr[now][i])
{
fail[tr[now][i]]=tr[fail[now]][i];
flag[tr[now][i]]+=flag[tr[fail[now]][i]];
//注意传递flag标记的分值
q.push(tr[now][i]);
}
else tr[now][i]=tr[fail[now]][i];
}
}
int main()
{
scanf("%d",&T);
for (int t=1;t<=T;t++)
{
tot=0;init(tot);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%s",s[i]);
for (int i=1;i<=m;i++)
scanf("%d",&tag[i]);
for (int i=1;i<=m;i++)
add(i);
acatm();
int maxi=0,maxj=0;
for (int i=0;i<=n;i++)
for (int j=0;j<=tot;j++)
dp[i][j]=-1;
dp[0][0]=0;
path[0][0][0]='\0';
//因为之后的path是由之前的path更新出来的所以只用初始化一个
for (int i=0;i<n;i++)
for (int j=0;j<=tot;j++)
{
if (dp[i][j]==-1) continue;
for (int k=0;k<26;k++)
{
int book=0;//标记此次是否找到了更优解
int tmp=tr[j][k];
int tmpv=dp[i][j]+flag[tmp];
if (tmpv > dp[i+1][tmp])
{
dp[i+1][tmp]=tmpv;
strcpy(path[i+1][tmp],path[i][j]);
//表示讲path[i][j]复制给path[i+1][tmp]
int tmpl=strlen(path[i+1][tmp]);
path[i+1][tmp][tmpl]=k+'a';
path[i+1][tmp][tmpl+1]='\0';
book=1;
}
else if (tmpv == dp[i+1][tmp])
{
int tmpl=strlen(path[i][j]);
path[i][j][tmpl]=k+'a';
path[i][j][tmpl+1]='\0';
if (strcmp(path[i][j],path[i+1][tmp])<0)
{
//strcmp(a,b)>0表示字符串a的字典序大于b的字典序,小于0则反之
strcpy(path[i+1][tmp],path[i][j]);
book=1;
}
path[i][j][tmpl]='\0';
}
if (book)
{
发现此次找到了临时的最优解,要将它和最最最优的解进行比较,看哪个更优一些
if (tmpv > dp[maxi][maxj]) maxi=i+1,maxj=tmp;
else if (tmpv == dp[maxi][maxj] && i+1<=maxi && strcmp(path[maxi][maxj],path[i+1][tmp])>0)
maxi=i+1,maxj=tmp;
}
}
}
if (dp[maxi][maxj]!=0) printf("%s\n",path[maxi][maxj]);
else printf("\n");
//注意题上要求无解的时候输出空行
}
}
上次忘写函数调了两个小时,这次又是忘记建字典树时把字母转成数字调了半个小时。。。QAQ感觉最近不适合写代码。。。