题面
题意
给出一个字符串(最多包含 a-f 6个字母),每个位置也都给出一个字符集,现在要求改变其排列的顺序,使其每个位置的字符否是给出该位置上字符集中的一个,输出字典序最小的合法字符串。
做法
首先想到的肯定是一个一个字符贪心的加入,问题就在于如何判断将一些字符放入剩下的位置,是否存在合法方案。
判断方法是这样的:
枚举所有字符集(2^6个),然后若存在一个字符集,其中还未放入的字符的总和大于这个字符集可以放入的位置的个数,则不合法,反之合法,这样就可以求解了。
代码
#include<iostream>
#include<cstdio>
#define N 100100
using namespace std;
int n,m,cnt[70],can[N],have[70];
string str,ans;
inline void del(int u,int v)
{
int i;
for(i=0; i<64; i++)
{
if(i&can[u]) have[i]--;
if(i&(1 << v)) cnt[i]--;
}
}
inline void add(int u,int v)
{
int i;
for(i=0; i<64; i++)
{
if(i&can[u]) have[i]++;
if(i&(1 << v)) cnt[i]++;
}
}
inline bool check()
{
int i;
for(i=0; i<64; i++)
{
if(cnt[i]>have[i]) return 0;
}
return 1;
}
int main()
{
ios::sync_with_stdio(0);
int i,j,tmp,p;
cin>>str;
n=str.size();
for(i=0; i<str.size(); i++)
{
cnt[1 << (str[i]-'a')]++;
}
for(i=1; i<64; i++)
{
for(j=i-1; j>=0; j--)
{
if((j&i)==j)
{
cnt[i]=cnt[j]+cnt[j^i];
}
}
}
cin>>m;
for(i=1; i<=n; i++) can[i]=63;
for(i=1; i<=m; i++)
{
cin>>p>>str;
tmp=0;
for(j=0; j<str.size(); j++)
{
tmp|=(1 << (str[j]-'a'));
}
can[p]&=tmp;
}
for(i=1; i<=n; i++)
{
for(j=0; j<64; j++)
{
if(can[i]&j) have[j]++;
}
}
if(!check())
{
puts("Impossible");
return 0;
}
for(i=1; i<=n; i++)
{
for(j=0; j<6; j++)
{
if((1 << j)&can[i])
{
del(i,j);
if(check())
{
ans+=(char)(j+'a');
break;
}
add(i,j);
}
}
}
cout<<ans;
}