C - 字典序最小的子序列 51nod1255
C - 字典序最小的子序列
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 1280000/640000 KB (Java/Others)
Submit Status
Problem Description
给出一个由a-z组成的字符串S,求他的一个子序列,满足如下条件:
1、包含字符串中所有出现过的字符各1个。
2、是所有满足条件1的串中,字典序最小的。
例如:babbdcc,出现过的字符为:abcd,而包含abcd的所有子序列中,字典序最小的为abdc。
1、包含字符串中所有出现过的字符各1个。
2、是所有满足条件1的串中,字典序最小的。
例如:babbdcc,出现过的字符为:abcd,而包含abcd的所有子序列中,字典序最小的为abdc。
Input
单样例输入。
每个输入1行字符串S,所有字符均为小写,字符串的长度为L。(1 ≤ L ≤ 107)。
所有样例中只有一个样例 L > 105
Output
输出包含S中所有出现过的字符,每个字符各1个,并且字典序最小的S的子序列。
Sample Input
babbdcc
Sample Output
abdc
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int N=1e7+10; 5 const int INF=0x3f3f3f3f; 6 int cas=1,T; 7 char s[N],ans[N]; 8 int a[N],n; 9 vector<int>q[26]; 10 int main() 11 { 12 // freopen("1.in","w",stdout); 13 // freopen("21.in","r",stdin); 14 // freopen("21.out","w",stdout); 15 // scanf("%d",&T); 16 while(scanf("%s",s)==1) 17 { 18 int n=0; 19 for(;s[n];n++) q[s[n]-'a'].push_back(n); 20 a[n]=0; 21 for(int i=n-1;i>=0;i--) 22 { 23 int id=s[i]-'a'; 24 a[i]=a[i+1]|(1<<id); 25 } 26 int d=0,an=0,id=0; 27 while(d!=a[0]) 28 { 29 for(int i=0;i<26;i++) if((d&(1<<i))==0 && q[i].size()) 30 { 31 auto it=lower_bound(q[i].begin(),q[i].end(),id); 32 if(it!=q[i].end()) 33 { 34 if((d|a[*it])==a[0]) 35 { 36 ans[an++]=i+'a'; 37 d|=1<<i; 38 id=*it; 39 break; 40 } 41 } 42 } 43 } 44 ans[an]=0; 45 puts(ans); 46 } 47 // printf("time=%.3lf\n",(double)clock()/CLOCKS_PER_SEC); 48 return 0; 49 }
题解:
用一个int的bit表示每个字母是否存在,设字母集合为U,从后往前扫一遍算出存在后缀和,即a[i]表示这个数后面有哪些字母
再从前往后扫一遍,记录已有字母的集合U1,对于位置i,每次取能与U1,a[i]并起来能得到全集U 的最小的字母,最后字母集合U1为所求结果