题解:CF1739F Keyboard Design
Mr_Terminator · · 题解
思维题。码量题。
首先一个单次如果能被便捷的敲出来,当且仅当它的所有字母两边的字母都与它在键盘上相邻。这样我们可以对于每个单词整理出一个计入贡献时字母在键盘上的相邻关系。接下来我们针对
我们很容易注意到,如果一个单词要求一个字母周围有大于等于
数学归纳法。只有两个字母的字符串显然满足条件。假设
-
进行奇偶性分析可得,
S 必然存在恰好0 或2 个字母临近于仅1 个不同字母。 -
把字母作为点,
S 中相邻的字母之间连一条边。如果S 没有这样的字母,那么所有出现过的字母将会连成一个环(道理很简单:所有出现过字母连通),而如果S 有2 个这样的字母,所有出现过字母会形成一条链。 -
如果在
S 后面增加一个字符c ,相当于如果S 的最后一个字符与c 没有连过边就连一条,此时最后一个字符的出度必然为1 ,也即所有字母形成一条链,那么c 要么没有出现在S 中,要么是另一个链头,连完后形成一个环。如果S 最后一个字符与c 已经连过边了,那么原先是环现在还是环,原先是链现在还是链。 -
操作完之后最终肯定仍然是环或者链,那么
S+c 也满足上述性质。
扯那么多是干嘛呢?如果一个单词(没有出现邻近于
由于不同字母数量非常少,时间限制又很大,一条链又无法正着反着同时出现在键盘里,我们可以将所有合法单词的链正反一起建一个
代码实现非常长,相当适合锻炼码力:
#include<bits/stdc++.h>
using namespace std;
struct node{
int son[13],fail,val;
}vert[18005];
int n,c[2005],len[2005],ths[2005][13],pt,clnk[12],ctow[12][12],pt2,rot,dp[4096][18005],H[4096][18005][2],ans,hs1,hs2;
int nw(){
return++pt2;
}
int nwvert(int now,int num){
if(vert[now].son[num]==0){
return vert[now].son[num]=nw();
}
return vert[now].son[num];
}
int Link(int now,int num){
if(vert[now].son[num]==0){
if(vert[now].fail==0)return rot;
return Link(vert[now].fail,num);
}
return vert[now].son[num];
}
void bfs(){
queue<int>q;q.push(rot);
while(!q.empty()){
int now=q.front();q.pop();
for(int o=1;o<=12;o++){
if(vert[now].son[o]!=0){
vert[vert[now].son[o]].fail=now==0?1:Link(vert[now].fail,o);
vert[vert[now].son[o]].val+=vert[vert[vert[now].son[o]].fail].val;
q.push(vert[now].son[o]);
}
}
}
}
int main(){
memset(dp,0xb0,sizeof(dp));
cin>>n;
for(int i=1;i<=n;i++){
int val;string s;bool satis1=0,satis2=1;cin>>val>>s;
for(int o=0;o<12;o++){
clnk[o]=0;
for(int oo=0;oo<12;oo++)ctow[o][oo]=0;
}
for(int o=0;o<s.size()-1;o++){
if(!ctow[s[o]-'a'][s[o+1]-'a'])clnk[s[o]-'a']++;
ctow[s[o]-'a'][s[o+1]-'a']=1;
if(!ctow[s[o+1]-'a'][s[o]-'a'])clnk[s[o+1]-'a']++;
ctow[s[o+1]-'a'][s[o]-'a']=1;
}
for(int o=0;o<12;o++){
if(clnk[o]==1)satis1=1;
if(clnk[o]>2)satis2=0;
}
if(!(satis1&satis2))continue;
pt++;c[pt]=val;
for(int o=0;o<12;o++){
if(clnk[o]!=1)continue;
for(int oo=o,Lst=-1;;){
ths[pt][++len[pt]]=oo+1;
int Nxt=-1;
for(int ooo=0;ooo<12;ooo++){
if(ooo==Lst)continue;
if(ctow[oo][ooo]){
Nxt=ooo;break;
}
}
if(Nxt==-1)break;
Lst=oo;oo=Nxt;
}
break;
}
}
for(int i=1;i<=pt;i++){
len[i+pt]=len[i];c[i+pt]=c[i];
for(int j=1;j<=len[i];j++){
ths[i+pt][j]=ths[i][len[i]+1-j];
}
}
pt*=2;rot=nw();
for(int i=1;i<=pt;i++){
int cur=rot;
for(int j=1;j<=len[i];j++){
cur=nwvert(cur,ths[i][j]);
}
vert[cur].val+=c[i];
}
bfs();dp[0][1]=0;
for(int zt=0;zt<4096;zt++){
for(int o=1;o<=12;o++){
if((zt&(1<<o-1))!=0)continue;
for(int i=1;i<=pt2;i++){
int Nxt=Link(i,o);
dp[zt^(1<<o-1)][Nxt]=max(dp[zt^(1<<o-1)][Nxt],dp[zt][i]+vert[Nxt].val);
if(H[zt^(1<<o-1)][Nxt][1]==0||dp[zt][i]+vert[Nxt].val==dp[zt^(1<<o-1)][Nxt]){
H[zt^(1<<o-1)][Nxt][0]=zt;H[zt^(1<<o-1)][Nxt][1]=i;
}
}
}
}
hs1=4095;
for(int i=1;i<=pt2;i++){
ans=max(ans,dp[4095][i]);
if(dp[4095][i]==ans)hs2=i;
}
while(hs1!=0){
int xornum=hs1^H[hs1][hs2][0];
for(int o=1;o<=12;o++){
if(xornum==(1<<o-1)){
cout<<char('a'+o-1);break;
}
}
hs2=H[hs1][hs2][1];hs1^=xornum;
}
return 0;
}