CF1009G Allowed Letters
题意:
给你一个长为n的串,字符集'a'~'f'。你可以重排这个串,满足指定m个位置上只能放特定的字符,m个位置以及字符集会给出。求字典序最小的串?
$n,m\leq 10^5.$
题解:
稍微需要那么一点技巧的贪心。
贪心策略比较显然,无非就是从左往右放尽可能小的字符,同时保证当前位置之后有合法解。
考虑预处理a[i]:i位置可以放的字符集;cnt[i]:集合i的字符在整个串中出现的次数;b[i][j]:i~n位置中a[]被集合j包含的个数。每次判断一个字符是否可行,只要枚举任意一个集合j,如果j集合中所有可用的字符在之后每个放一个位置还不够的话,说明不合法。
依次贪心下去即可。
复杂度$\mathcal{O}(6\times 2^6\times n)$。
code:
1 #include<bits/stdc++.h> 2 #define rep(i,x,y) for (int i=(x);i<=(y);i++) 3 #define per(i,x,y) for (int i=(x);i>=(y);i--) 4 #define ll long long 5 #define inf 1000000001 6 #define y1 y1___ 7 using namespace std; 8 ll read(){ 9 char ch=getchar();ll x=0;int op=1; 10 for (;!isdigit(ch);ch=getchar()) if (ch=='-') op=-1; 11 for (;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0'; 12 return x*op; 13 } 14 #define N 100005 15 #define M 70 16 int n,m,a[N],b[N][M],tmp[M],cnt[M];char s[N],s2[N],ans[N]; 17 int main(){ 18 scanf("%s",s+1);n=strlen(s+1); 19 m=read(); 20 while (m--){ 21 int k=read(),l;scanf("%s",s2+1);l=strlen(s2+1); 22 rep (i,1,l) a[k]|=1<<s2[i]-'a';//该位置可以放的字符集 23 } 24 rep (i,1,n) if (!a[i]) a[i]=(1<<6)-1; 25 rep (i,1,n){ 26 int x=s[i]-'a'; 27 rep (j,0,(1<<6)-1) if (j>>x&1) cnt[j]++;//cnt[i]:集合i的字符在整个串中出现的次数 28 } 29 per (i,n,1) rep (j,0,(1<<6)-1){//b[i][j]:i~n位置中a[]被集合j包含的个数 30 if ((j&a[i])==a[i]) tmp[j]++; 31 b[i][j]=tmp[j]; 32 } 33 rep (p,1,n-1){ 34 bool flag=0; 35 rep (i,0,5) if (cnt[1<<i]&&(a[p]>>i&1)){//贪心,保证之后还能放 36 bool chk=1; 37 rep (j,0,(1<<6)-1) if (cnt[j]-(j>>i&1)<b[p+1][j]){//j集合中每个字符放一个位置还不够,不合法 38 chk=0; 39 break; 40 } 41 if (chk){ 42 flag=1;ans[p]=i+'a'; 43 rep (j,0,(1<<6)-1) if (j>>i&1) cnt[j]--; 44 } 45 } 46 if (!flag){puts("Impossible");exit(0);} 47 } 48 bool flag=0; 49 rep (i,0,5) if (cnt[1<<i]&&(a[n]>>i&1)){ 50 flag=1;ans[n]=i+'a'; 51 break; 52 } 53 if (!flag){puts("Impossible");exit(0);} 54 ans[n+1]='\0'; 55 puts(ans+1); 56 return 0; 57 }