Educational Codeforces Round 96 (Rated for Div. 2) E. String Reversal
題意
給定一個長度為\(n\)的字符串,每次操作可以交換兩個相鄰字符,問將原串倒置所需要的最小操作數。
限制
\(2\leq n\leq 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\times 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<<'\n';
return 0;
}