[关闭]
@ysner 2018-06-08T21:31:44.000000Z 字数 1440 阅读 1988

密码

贪心 树状数组


题面

给定一字符串,现可进行次交换相邻字符的操作,询问操作完后可能的最小字典序的字符串。

解析

显然从前往后枚举字符,暴力把可能的最小字符移到当前位置即可。
最坏复杂度,期望
但复杂度不太满,如数据过水可获得

  1. scanf("%lld",&n);
  2. scanf("%s",a+1);len=strlen(a+1);
  3. fp(i,1,len)
  4. {
  5. re ll s=min(i+n,len),pos=i;char zsy=a[i];
  6. fp(j,i,s)
  7. if(a[j]<zsy||(a[j]==zsy&&j<pos)) zsy=a[j],pos=j;
  8. n-=(pos-i);
  9. zsy=a[pos];fq(j,pos,i+1) a[j]=a[j-1];a[i]=zsy;
  10. if(!n) break;
  11. }
  12. printf("%s\n",a+1);

发现寻找可行域内最小字符很耗时间。
我们可以用树状数组维护每个字符前需要交换的字符数,用链表维护个字符出现的第一个位置。
关键在于交换字符后,我们不关心字符间的顺序,只关心字符个数。
交换完后把本在后面的字符删掉,因其不再参与交换。

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cstdlib>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define min(a,b) ((a)<(b)?(a):(b))
  11. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  12. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  13. using namespace std;
  14. const int N=2e5;
  15. ll n,len,h[N],nxt[N],t[N];
  16. char a[N];
  17. il void add(re ll x,re ll w){for(;x<=len;x+=x&-x) t[x]+=w;}
  18. il ll Query(re ll x){re ll ans=0;for(;x;x-=x&-x) ans+=t[x];return ans;}
  19. int main()
  20. {
  21. freopen("pasuwado.in","r",stdin);
  22. freopen("pasuwado.out","w",stdout);
  23. scanf("%lld",&n);
  24. scanf("%s",a+1);len=strlen(a+1);
  25. fq(i,len,1) nxt[i]=h[a[i]-96],h[a[i]-96]=i;//为26个字母按从前往后顺序分别建立位置链表,原先的h移到next
  26. fp(i,1,len) add(i,1);
  27. fp(i,1,len)
  28. {
  29. fp(j,1,26)
  30. {
  31. re int pos=h[j];
  32. if(!pos) continue;
  33. re int sum=Query(pos-1);
  34. if(n>=sum)
  35. {
  36. n-=sum;
  37. add(pos,-1);
  38. h[j]=nxt[pos];//改变移完的字符的第一位
  39. printf("%c",j+96);
  40. break;
  41. }
  42. }
  43. }
  44. puts("");
  45. fclose(stdin);
  46. fclose(stdout);
  47. return 0;
  48. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注