String Reversal

Educational Codeforces Round 96 (Rated for Div. 2) - E. String Reversal#

跳转链接

题目描述#

定义一个操作为交换字符串中相邻的两个字母
给定一个只含有小写字母的字符串 求出从原字符串到翻转之后的字符串需要多少次操作

输入#

5
aaaza

输出#

2

node#

在第一个示例中,您必须交换第三个和第四个元素,因此字符串变为"aazaa"。
然后你必须交换第二个和第三个元素,所以字符串变成了"azaaa"。 因此,可以在两次交换中反转字符串。

分析#

我们可以发现每次交换相邻的两个字符 这很像冒泡排序 (冒泡排序中最少的交换次数为 逆序对数
因此这道题就是让我们求逆序对数了,然而这道题有相同的字符出现的可能,就造成了有一些细节问题需要
处理

  • 对于样例aaaza 我们可以把这个字符串等效为12345 然而逆序的如果是54321 这样显然是不对的, 因为我们相同
    的字符在移动的时候肯定是最靠近的去移动,而不是最远的去移动 因此这个逆序应该是14235 这样显然就会有最小的
    操作次数了.
  • 简单来说就是对于逆序的字符串来说, 某个字符从左到右的数字应该是从小到大排序的 比如样例的逆序为azaaa 显然我们
    最左边的那个a 应该是我们所有a所代表的数字中的最小的 因此就是1 后面同理 (这样可以在满足题意的情况下最优
    因此呢这道题就变成了一道求逆序对的题 我们可以用树状数组求解.

这里简单讲一下如何用树状数组求逆序对

  • 我们把一个数组翻转之后, 我们每次查询这个数字之前的数字出现了多少次 就是原数组中以这个数字开头的逆序对数
  • 我们倒序循环原来的数组, 每次求a[i]的前n项和, 然后在树状数组中的这个下标+1 即可
    分享两道树状数组的例题
  • Acwing 楼兰图腾
  • 洛谷 逆序对

本题的代码

AC_CODE#

#include <iostream>
#include <cstdio>
#include <vector>
#define pb push_back
#define lowbit(i) i & -i

using namespace std;

const int N = 2e5 + 10;

int n;
int tr[N];
long long ans;
vector<int> a[26], id(26);

void add(int x, int c) {
    for(int i = x; i <= n; i += lowbit(i)) 
        tr[i] += c;
}

int sum(int x) {
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}
int main() {
    cin >> n;
    string s; cin >> s;
    for(int i = 0; i < n; i ++ ) a[s[i] - 'a'].pb(i + 1);
    for(int i = 0; i < n; i ++ ) {
        int p = s[i] - 'a';
        ans += sum(a[p].back());
        add(a[p].back(), 1);
        a[p].pop_back();
    }
    cout << ans << endl;
}
posted @   ccz9729  阅读(459)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩