题意:中文的题目,意思就是说有很多串,每个串都有权值,权值为999的串必须出现,-999的串必须不出现。权值在-999~999之间。
然后必须出现的串不超过8个。然后给一个全为小写目标串,问最少需要删除多少个字母才能够保证必须出现的串都出现,次数一样保证权值最大。输出次数和权值。
然后根据样例,那些必须出现的串,其实权值是0。
思路:
很明显一开始建自动机构成trie图,但是需要注意的就是mark和sum的更新。个人是把所有中间的节点的sum全部赋值成了-inf。
接着只有8个必须出现的串,所以必须要状压一下。
所以最后就是dp[i][j][k][l],处理了i个字母,在j这个节点,k这个状态。l=0代表这个字母不删,l=1代表这个字母删。
然后是个结构体存两个值次数和评分。然后就是更新的时候有点麻烦。
然后注意一下,最后的评分有可能是负数。
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#include"map"
#include"string"
#define inf 999999
using namespace std;
struct trie
{
int mark,id,sum;
trie *next[27],*fail;
trie()
{
mark=0;
sum=-inf;
memset(next,0,sizeof(next));
fail=NULL;
}
};
trie *root,*node[1700];
int triecont;
void init(char *v,int k,int x)
{
trie *p=root;
for(int i=0; v[i]; i++)
{
int tep=v[i]-'a';
if(p->next[tep]==NULL)
{
p->next[tep]=new trie();
p->next[tep]->id=triecont;
node[triecont++]=p->next[tep];
}
p=p->next[tep];
}
if(k==1) //必须出现
{
p->mark=(1<<x);
p->sum=0;
}
else if(k==-1) p->mark=-1; //必须不出现
else p->sum=x; //无特定要求
}
void getac()
{
queue<trie*>q;
q.push(root);
while(!q.empty())
{
trie *p=q.front();
q.pop();
for(int i=0; i<26; i++)
{
if(p->next[i]==NULL)
{
if(p==root) p->next[i]=root;
else p->next[i]=p->fail->next[i];
}
else
{
if(p==root) p->next[i]->fail=root;
else p->next[i]->fail=p->fail->next[i];
q.push(p->next[i]);
if(p!=root)
{
if(p->next[i]->mark==-1 || p->next[i]->fail->mark==-1) //更新必须不出现
p->next[i]->mark=-1;
else
{
p->next[i]->mark|=p->next[i]->fail->mark;
if(p->next[i]->fail->sum!=-inf) //更新权值
{
if(p->next[i]->sum==-inf) p->next[i]->sum=0;
p->next[i]->sum+=p->next[i]->fail->sum;
}
}
}
}
}
}
}
struct Dp
{
int cs,pf;
} dp[2][1605][259][2]; //这里必须要滚动,不然MLE
int main()
{
int t,cas=1;
cin>>t;
while(t--)
{
triecont=0;
root=new trie();
node[triecont]=root;
root->id=triecont++;
int n,cnt=0;
scanf("%d",&n);
while(n--)
{
char x[177];
int y;
scanf("%s%d",x,&y);
if(y==-999) init(x,-1,0);
else if(y==999) init(x,1,cnt++);
else init(x,0,y);
}
getac();
char fuck[123];
scanf("%s",fuck);
int len=strlen(fuck);
for(int i=0; i<triecont; i++)
for(int j=0; j<(1<<cnt); j++)
for(int k=0; k<2; k++)
{
dp[0][i][j][k].cs=inf;
dp[0][i][j][k].pf=-inf;
}
dp[0][0][0][0].cs=dp[0][0][0][1].cs=0;
dp[0][0][0][0].pf=dp[0][0][0][1].pf=0;
for(int i=1; i<=len; i++)
{
for(int j=0; j<triecont; j++)
for(int k=0; k<(1<<cnt); k++)
for(int l=0; l<2; l++)
{
dp[i%2][j][k][l].cs=inf;
dp[i%2][j][k][l].pf=-inf;
}
for(int j=0; j<triecont; j++)
{
for(int k=0; k<(1<<cnt); k++)
{
if(dp[1-i%2][j][k][0].cs==dp[1-i%2][j][k][1].cs && dp[1-i%2][j][k][0].cs==inf) continue;
for(int l=0; l<2; l++)
{
if(node[j]->next[fuck[i-1]-'a']->mark!=-1) //这里需要注意,有的情况是必须删除的,所以不删除的情况是要判断一下的。
{
trie *p=node[j]->next[fuck[i-1]-'a'];
int tep=p->mark|k;
Dp cur=dp[i%2][p->id][tep][0],now;
if(dp[1-i%2][j][k][0].cs<dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][0];
else if(dp[1-i%2][j][k][0].cs>dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][1];
else
{
now.cs=dp[1-i%2][j][k][1].cs;
now.pf=max(dp[1-i%2][j][k][0].pf,dp[1-i%2][j][k][1].pf);
}
if(p->sum!=-inf)
{
if(now.pf==inf) now.pf=0;
now.pf+=p->sum;
}
if(now.cs<cur.cs) cur=now;
else if(now.cs==cur.cs) cur.pf=max(now.pf,cur.pf);
dp[i%2][p->id][tep][0]=cur;
}
Dp cur=dp[i%2][j][k][1],now; //这里是这个位置删除的更新
if(dp[1-i%2][j][k][0].cs<dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][0];
else if(dp[1-i%2][j][k][0].cs>dp[1-i%2][j][k][1].cs) now=dp[1-i%2][j][k][1];
else
{
now.cs=dp[1-i%2][j][k][1].cs;
now.pf=max(dp[1-i%2][j][k][0].pf,dp[1-i%2][j][k][1].pf);
}
now.cs++;
if(now.cs<cur.cs) cur=now;
else if(now.cs==cur.cs) cur.pf=max(now.pf,cur.pf);
dp[i%2][j][k][1]=cur;
}
}
}
}
Dp ans;
ans.cs=inf;
ans.pf=-inf;
for(int i=0; i<triecont; i++)
{
Dp now;
if(dp[len%2][i][(1<<cnt)-1][0].cs<dp[len%2][i][(1<<cnt)-1][1].cs) now=dp[len%2][i][(1<<cnt)-1][0];
else if(dp[len%2][i][(1<<cnt)-1][0].cs<dp[len%2][i][(1<<cnt)-1][1].cs) now=dp[len%2][i][(1<<cnt)-1][1];
else
{
now.cs=dp[len%2][i][(1<<cnt)-1][1].cs;
now.pf=max(dp[len%2][i][(1<<cnt)-1][0].pf,dp[len%2][i][(1<<cnt)-1][1].pf);
}
if(now.cs<ans.cs) ans=now;
else if(now.cs==ans.cs) ans.pf=max(now.pf,ans.pf);
}
printf("Case %d: ",cas++);
if(ans.cs==inf) puts("Banned");
else printf("%d %d\n",ans.cs,ans.pf);
}
return 0;
}