51nod_1255字典序最小的子序列
作为贪心算法的某道例题,赶脚药丸啊。。这么简单的代码重构第三遍才过。。。
首先是贪心算法思想,
1,证明贪心算法有效性:
贪心策略,使用栈结构实现,遍历输入串中所有元素,对于某个元素有如下两种情况:
情况A:如果栈内已经有该元素,则废弃该元素。
情况B:如果栈内没有该元素,则废弃栈内所有“并非最后一次出现”且字典序大于当该元素的字母,遇到第一个不合法的字母则停止废弃。
对于栈内元素,我们认为任何“不被最后一次出现元素分割”的连续元素都相当按照字典序的相对大小排列整齐。基于此设定,讨论任何一个元素:
情况A:对于任何一个非最后一次出现的元素,如果大于当前元素,意味着该元素可以再后面再出现一次,因此可以废弃。且因为新插入的“当前元素”字典序最小,因而认为“原油序列可以再后面重现”且“任何废弃元素后面可以接的元素,该元素后面都可以接”。所以“如果有最优解,则一定包含最小的元素”
情况B:对于最后一次出现的元素,因为其不可能在后面出现,所以不可以将其废弃,对于该元素前面的元素,因为相对该元素较小,也不可废弃。
PS:注意一个坑,使用某元素出现此时来判断他未来会不会再出现比较好。
show the code:
#include<bits/stdc++.h> using namespace std; const long long MAXN=100000+233; char str[MAXN]; int times[26]; set<char> s1; stack<int> stack1; int main() { // cin.sync_with_stdio(false); memset(times,0,sizeof(times)); cin>>str; int len=strlen(str); for(int i=0;i<len;++i) { times[str[i]-'a']++; } for(int i=0;i<len;++i) { char ncha=str[i]; times[ncha-'a']--; if(s1.count(ncha))continue; while(!stack1.empty()) { int pos=stack1.top(); char cha=str[pos]; if(cha<ncha||times[cha-'a']==0)break; if(cha>ncha) { s1.erase(cha); stack1.pop(); } } s1.insert(ncha); stack1.push(i); } string str1; while(!stack1.empty()) { str1.push_back(str[stack1.top()]); stack1.pop(); } int len1=str1.length(); for(int i=len1-1;i>=0;--i) { cout<<str1[i]; }cout<<endl; }