Codeforces 1430E - String Reversal (貪心、暴力)


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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM