51nod 1255 字典序最小的子序列【貪心】
阿新 • • 發佈:2019-02-06
給出一個由a-z組成的字串S,求他的一個子序列,滿足如下條件:
1、包含字串中所有出現過的字元各1個。 2、是所有滿足條件1的串中,字典序最小的。 例如:babbdcc,出現過的字元為:abcd,而包含abcd的所有子序列中,字典序最小的為abdc。 Input
1、包含字串中所有出現過的字元各1個。 2、是所有滿足條件1的串中,字典序最小的。 例如:babbdcc,出現過的字元為:abcd,而包含abcd的所有子序列中,字典序最小的為abdc。 Input
輸入1行字串S,所有字元均為小寫,字串的長度為L。(1 <= L <= 100000)。Output
輸出包含S中所有出現過的字元,每個字元各1個,並且字典序最小的S的子序列。Input示例
babbdccOutput示例
abdc
思路:
1、我們建立一個棧,用來存放解。
對於這個字串,我們從第一個字元開始掃:
①如果這個棧此時為空,那麼將這個字元丟進去。
②如果棧此時不為空,而且當前這個字元已經在棧中,跳過。
③如果棧此時不為空,而且當前這個字元不在棧中,我們分兩種情況討論:1.如果這個字元比棧頂大,那麼直接丟到棧頂即可。2.如果這個比棧頂小,那麼我們判斷此時棧頂在這個字元後邊還是否存在。如果後邊還有,那麼對應將棧頂彈出,直到不能彈出為止,再將這個字元丟到棧頂。
這樣我們就能做到儘可能的貪心。
2、考慮到我們如果O(n^2)判斷當前字元後邊是否還存在這個字元,是很容易TLE的。所以我們O(n)維護當前這個字元在字串中是第幾次出現的,如果是最後一次,那麼顯然這個字元後邊就不再存在了。這個方法還是很好實現的。
3、過程中細節較多,我們注意千萬要維護好每個字元後邊是否還存在這個字元這個條件。最後將所有字元彈出棧,逆序輸出。
Ac程式碼:
#include<stdio.h> #include<string.h> #include<stack> using namespace std; char a[100060]; char ans[300]; int use[300]; int have[300]; int num[100060]; int contz[300]; int main() { while(~scanf("%s",a)) { int n=strlen(a); memset(contz,0,sizeof(contz)); memset(num,0,sizeof(num)); memset(use,0,sizeof(use)); for(int i=0;i<256;i++)have[i]=1; for(int i=0;i<n;i++) { contz[a[i]]++; num[i]=contz[a[i]]; } stack<char >s; for(int i=0;i<n;i++) { if(s.size()==0) { s.push(a[i]); use[a[i]]=1; if(num[i]==contz[a[i]])have[a[i]]=0; } else { if(use[a[i]]==1) { if(num[i]==contz[a[i]])have[a[i]]=0; continue; } else if(a[i]<s.top()) { while(!s.empty()) { if(a[i]<s.top()&&have[s.top()]==1) { use[s.top()]=0; s.pop(); } else break; } } s.push(a[i]); use[a[i]]=1; if(num[i]==contz[a[i]])have[a[i]]=0; } } int cont=0; while(!s.empty()) { ans[cont++]=s.top(); s.pop(); } for(int i=cont-1;i>=0;i--) { printf("%c",ans[i]); } printf("\n"); } }