题目描述
哪里有压迫,哪里就有反抗。
moreD 的宠物在法庭的帮助下终于反抗了。作为一只聪明的宠物,他打算把
魔法使 moreD 的魔法书盗去,夺取 moreD 的魔法能力。但 moreD 怎么会让自己的
魔法书轻易地被盗取?moreD 在魔法书上设置了一个密码锁,密码锁上有一个问
题。
施以斯卧铺魔法吧,你有 M 次机会,如此将得完美密码。
然后是一串小写字母串。
moreD 的宠物斯卧铺魔法就是施法时的字符串其中相邻两位交换。
而 moreD 对于完美密码的定义自然是最小字典序了。
请帮助 moreD 的宠物,想出密码吧。
输入格式
第一行一个整数 M,表示操作次数。
第二行一串小写字母组成的字符串 S,如题目所示。
输出格式
输出完美密码
输入样例
3
dcba
输出样例
adcb
数据范围
对于 30%的数据|S|≤10
对于 60%的数据|S|≤3,000
对于 100%的数据 8≤|S|≤100,000,M≤(|S|-8)^2+2
后记
宠物最终战胜了 moreD,和自己的宠物快乐地生活着。
样例解释
先对第 3,4 两位施法,字符串变成 dcab,然后对第 2,3 两位施法,字符串
变成 dacb,最后对第 1,2 两位施法,字符串变成 adcb。
一眼望过去
很明显是个贪心啊,要求在一定的步骤内通过两两交换使得字典序最小.
解法
针对30%的数据:
暴力dfs。
针对60%的数据:
贪一下心吧。由于题目要求字典序,可以贪心地从前到后确定每一位的字母。字母肯定是从小到大地枚举,操作距离内的枚举字母的话肯定会贪心地把这个字母换入目标位置。而选择枚举的字母就是贪心地选择尽量前的字母。
针对100%的数据:
每次找每种字母最前的那个,再对他进行往前移,这样可以用最小的步骤。可以用vector或链表。然后我们要统计交换的代价,因为是动态改变的,我们就可以用数据结构来维护,比如树状数组或线段树.
参考代码
#include<bits/stdc++.h>
const int N=1e5+10;
typedef long long ll;
using namespace std;
int n;
int top[26];
ll m,tre[N];
char s[N],ans[N];
vector<int>f[26];
inline int lowbit(int k){
return k&(-k);
}
inline void add(int k,int v) {
for(int i=k; i<=n; i+=lowbit(i)) tre[i]+=v;
}
inline ll ask(int i) {
ll sum=0;
while(i) {
sum+=tre[i];
i-=lowbit(i);
}
return sum;
}
int main() {
freopen("pasuwado.in", "r", stdin);
freopen("pasuwado.out", "w", stdout);
cin>>m;
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1; i<=n; i++) {
f[s[i]-'a'].push_back(i);
if(i==1) add(i,0);
else add(i,1);
}
for(int i=1; i<=n; i++) {
for(int j=0; j<26; j++) {
if(top[j]>=f[j].size()) continue;
int t=f[j][top[j]];
ll cost=ask(t);
if(cost<=m) {
m-=cost;
add(t+1,-1);
top[j]++;
ans[i]=j+'a';
break;
}
}
}
for(int i=1; i<=n; i++) printf("%c",ans[i]);
cout<<endl;
return 0;
}