您的位置:首页 > 编程语言

【Hdu 4117】GRE Words

2016-01-21 21:35 330 查看
Problem Description

Recently George is preparing for the Graduate Record Examinations (GRE for short).
Obviously the most important thing is reciting the words.
Now George is working on a word list containing N words.
He has so poor a memory that it is too hard for him to remember all of the words on the list.
But he does find a way to help him to remember.
He finds that if a sequence of words has a property that for all pairs of neighboring words,
the previous one is a substring of the next one,
then the sequence of words is easy to remember.
So he decides to eliminate some words from the word list first to make the list easier for him.
Meantime, he doesn't want to miss the important words.
He gives each word an importance,
which is represented by an integer ranging from -1000 to 1000,
then he wants to know which words to eliminate to maximize the sum of the importance of remaining words.
Negative importance just means that George thought it useless and is a waste of time to recite the word.
Note that although he can eliminate any number of words from the word list, he can never change the order between words.
In another word, the order of words appeared on the word list is consistent with the order in the input.
In addition, a word may have different meanings, so it can appear on the list more than once,
and it may have different importance in each occurrence.


Input

The first line contains an integer T(1 <= T <= 50), indicating the number of test cases.
Each test case contains several lines.
The first line contains an integer N(1 <= N <= 2 * 104), indicating the number of words.
Then N lines follows, each contains a string Si and an integer Wi, representing the word and its importance.
Si contains only lowercase letters.
You can assume that the total length of all words will not exceeded 3 * 105.


Output

For each test case in the input, print one line: "Case #X: Y",
where X is the test case number (starting with 1) and Y is the largest importance of the remaining sequence of words.


Sample Input

1
5
a 1
ab 2
abb 3
baba 5
abbab 8


Sample Output

Case #1: 14


题目大意

给出一串单词,每个有一个权值。顺序不变的情况下,删掉一些,使得相邻两单词,前一个是后一个的子串。
同时要求使得剩余单词权值和最大。求最大是多少。


题解

AC自动机+fail树+线段树+DP
f[i]表示前i个单词的最大权值和
f[i]=max(f[v]+max(val[i],0)),v是i的子串
我们注意到v是i的子串这个条件还可以表示为v可以是第i个单词的每一个前缀的fail值,然后若我们之间去找v比较麻烦,
反过来想,假设f[i]求出来了,那f[i]会影响所有以它为后缀的字符串(即fail树上它的子树),
然后我们求出fail树的dfs序(f[i]在上面影响一段区间),
所以每次求出f值后的更新就相当于区间更新,查询就是单点查询,这个用线段树维护就好了。


代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <ctime>

using namespace std;

#define rep(i,l,r) for(i=l;i<=r;i++)
#define ser(i,r,l) for(i=r;i>=l;i--)
#define INF 300005
#define inf 1000000007

typedef long long ll;
priority_queue<int >QwQ;
queue<int >Q;

int num=0,cnt=0,tot,l=0,T,n,ans=0;
int to[INF<<1],h[INF<<1],s[INF],t[INF][27],in[INF],fail[INF],L[INF],R[INF],mx[INF*4],A[INF*4],v[INF];
char s1[INF],s2[INF];
int read()
{
int k=0,f=1;
char ch;
while(ch<'0' || ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')k=(k<<1)+(k<<3)+ch-'0',ch=getchar();
return k*f;
}
void New(int x)
{
int i,j,k;
rep(i,1,26)t[x][i]=0;
fail[x]=0;
}
void hah(int x,int y)
{
to[++l]=y,h[l]=s[x],s[x]=l;
}
void Insert(int x)
{
int i,j,k,l1,now=1;
scanf("%s",s1);
l1=strlen(s1);
v[x]=read();
rep(i,0,l1-1){
k=s1[i]-'a'+1;
if(!t[now][k])t[now][k]=++tot,New(tot);
now=t[now][k];
s2[++num]=s1[i];
}
in[num]=x;
}
void build_fail()
{
int i,j,k,p;
fail[1]=0,Q.push(1);
while(!Q.empty()){
k=Q.front(),Q.pop();
rep(i,1,26)
if(t[k][i]){
p=fail[k];
while(!t[p][i])p=fail[p];
fail[t[k][i]]=t[p][i];
Q.push(t[k][i]);
}
}
}
void dfs(int x)
{
int i,j,k;
L[x]=++cnt;
for(i=s[x];i;i=h[i])
dfs(to[i]);
R[x]=++cnt;
}
void update(int v)
{
mx[v]=max(mx[v<<1],mx[v<<1|1]);
}
void pushdown(int v)
{
mx[v<<1]=max(mx[v<<1],A[v]);
mx[v<<1|1]=max(mx[v<<1|1],A[v]);
A[v<<1]=max(A[v<<1],A[v]);
A[v<<1|1]=max(A[v<<1|1],A[v]);
A[v]=0;
}
void build(int v,int l,int r)
{
int i,j,k,mid=(l+r)>>1;
mx[v]=A[v]=0;
if(l==r)return ;
build(v<<1,l,mid);
build(v<<1|1,mid+1,r);
update(v);
}
int Query(int v,int l,int r,int x)
{
int i,j,k,mid=(l+r)>>1;
if(l==r)return mx[v];
if(A[v])pushdown(v);
if(x<=mid)return Query(v<<1,l,mid,x);
else return Query(v<<1|1,mid+1,r,x);
update(v);
}
void Modify(int v,int l,int r,int x,int y,int z)
{
int i,j,k,mid=(l+r)>>1;
if(x<=l && r<=y){
mx[v]=max(mx[v],z);
A[v]=max(A[v],z);
return ;
}
if(A[v])pushdown(v);
if(x<=mid)Modify(v<<1,l,mid,x,y,z);
if(y>mid)Modify(v<<1|1,mid+1,r,x,y,z);
update(v);
}
void solve()
{
int i,j,k,mx=0,now=1;
ans=0;
build(1,1,cnt);
rep(i,1,num){
k=s2[i]-'a'+1;
while(!t[now][k])now=fail[now];
now=t[now][k];
mx=max(mx,Query(1,1,cnt,L[now]));
if(in[i]){
k=v[in[i]]>0?v[in[i]]:0;
ans=max(ans,mx+k);
Modify(1,1,cnt,L[now],R[now],mx+k);
mx=0,now=1;
}
}
printf("%d\n",ans);
}
void init()
{
int i,j,k=0;
T=read();
while(T--){
New(1),tot=1,num=0,cnt=0,l=0;
rep(i,1,26)t[0][i]=1;
memset(in,0,sizeof(in));
memset(s,0,sizeof(s));
n=read();
rep(i,1,n)Insert(i);
build_fail();
rep(i,1,tot)hah(fail[i],i);
dfs(0);
printf("Case #%d: ",++k);
solve();
}
}
void work()
{
int i,j,k;
}
int main()
{
init();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  编程 Hdu