Codeforces 1430 - E. String Reversal(字符串 + 逆序对)

链接 E. String Reversal

题意 :
给一个长度为 n 的字符串 , 每次操作可以交换两个相邻的字符 , 要把字符串倒置过来,最少要多少次操作 。
思路:

  1. 下次看到这种交换相邻字符的题目, 一定要先想到逆序对,这里要把字符串倒置 ,最直观的想法是 n * (n - 1) / 2, 把它倒置过来 , 但是字符串里肯定有相同的字符 , 所以我们只要让相同字符的相对位置不变 , 这样一定是最小的 ,求出变化后的编号 ,求逆序对就好了。

  2. 例如 aacbbb 一开始编号为 1 2 3 4 5 6 ,倒过来后 编号为 4 5 6 3 1 2 , 然后求逆序对。

  3. 然后晚上的 ccpc 测试赛 又来了一道差不多的题 , 把一个 01串变化成目标 01 串 ,只能交换相邻字符 , 问最少操作次数 ,解法完全一样。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int mod = 1e9 + 7;
int n,sum[maxn],cnt[30],ans[maxn];
ll anss;
char s[maxn];
vector< int > vec[30];
void update(int id, int l , int r , int rt){
    
    
    if(l == r){
    
    
        sum[rt] = 1;
        return ;
    }
    int mid= (l + r) / 2;
    if(id <= mid) update(id , l, mid , rt << 1);
    if(id > mid) update(id , mid + 1 , r , rt << 1 | 1);
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
int query(int L , int R , int l , int  r, int rt){
    
    
    int s = 0;
     if( L <= l && R >= r){
    
    
         return sum [rt];
     }
     int mid = (l + r) / 2;
     if(L <= mid) s += query(L, R, l , mid, rt << 1);
     if(R > mid) s += query(L,R, mid + 1  ,r , rt << 1 | 1); 
     return s;
}
int main (){
    
    
    scanf("%d%s",&n,s + 1);
    for(int i = 1; i <= n; i ++){
    
    
        int  x = s[i] - 'a';
        vec[x].push_back(i);
    }
    reverse(s + 1, s + n + 1);
    for(int i = 1; i <= n ; i ++){
    
    
        int  x = s[i] - 'a';
        ans[i] = vec[x][cnt[x] ++];
    }
    for(int i = 1; i <= n; i ++){
    
    
        anss += query(ans[i] , n ,1 , n, 1);
        update(ans[i] ,1 ,n , 1);
    }
    printf ("%lld\n",anss);
}

猜你喜欢

转载自blog.csdn.net/hddddh/article/details/109045004
今日推荐