Educational Codeforces Round 96 (Rated for Div. 2) E. String Reversal
题意
给定一个长度为(n)的字符串,每次操作可以交换两个相邻字符,问将原串倒置所需要的最小操作数。
限制
(2leq nleq 200000)
思路
先将原串(S)倒置存于新字符串(R)内,以下标(i)为循环变量,遍历字符串(R),每次将第(i)个位置的字符从剩余的字符串中移动出来并固定,剩余的字符串称作剩余串
那么就可以模拟字符移动,每次将字符(R[i])移动到剩余串的第一个位置,表示第(i)个位置的字符完成了移动
贪心可得,每次从剩余串中将下标最小的(R[i])移动到串首,操作次数最少
那么问题就转化成了记录每个字符在剩余串中出现的第一个位置,使用(26)个变量(pos)记录
先遍历原串(S),将每种字符第一次出现的位置记录在(pos)数组内,(cnt)数组记录每种字符出现的次数
遍历(R),对于每个字符(t),取出其在剩余串中第一次出现的位置(pos[t])
由于目的是将其移动到剩余串中的首位置,所以答案增加(pos[t]-1)
然后将原串中(pos[t])位置的一个字符删除,代表剩余串中删去一个字符
其后循环(26)个变量,由于删去字符后(pos[t])其后的字符下标全部(-1),故对于每个(pos[i]>pos[t]),需要让(pos[i]-1)
最后将(cnt[t]-1),如果(cnt[t])非零,则可以直接将指针(pos[t])向右移动寻找下一个字符(t)
最终复杂度为(O(n imes d),d=26)
程序
(405ms/2000ms)
#include<bits/stdc++.h>
using namespace std;
string S,R;
int n,pos[26],cnt[26];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>S;
S=" "+S;
R=S;
for(int i=1;i<=n;i++)
{
int t=S[i]-'a';
if(pos[t]==0)
pos[t]=i;
cnt[t]++;
}
long long ans=0; //注意答案可能超出int范围
for(int i=n;i;i--)
{
int t=R[i]-'a';
ans+=pos[t]-1;
S.erase(pos[t],1); //删去一字符
cnt[t]--;
for(int j=0;j<26;j++)
if(pos[j]>pos[t]) //对于每个位置大于pos[t]的
pos[j]--;
if(cnt[t]==0)
pos[t]=0;
else
{
while(S[pos[t]]-'a'!=t) //向右继续查找
pos[t]++;
}
}
cout<<ans<<'
';
return 0;
}