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