Educational Codeforces Round 96 E. String Reversal 树状数组+序列自动机

地址

题目大意

每次交换相邻的字符,使字符串反转。求最少的交换次数

思路

题目意思等价于交换相邻的字符使得两个字符串相等,两个字符串为原串和逆序串。交换相邻的字符代表着要贪心,每次把最近相等的字符移动过去,代价就是之间有多少个字符。举个例子:
注意:我们有一些约定。
1.下面出现的最前面指的是第一个还会变的位置,看下面解释理解。

2.当一个字符移动到最前面之后我们就把它删除。相当于每次找前面有多少个字符,要把已经用过的去掉。

abacc 反转后是 ccaba
先把最近的c移到最前面,交换了3次
把当前最近的c移动最前面,交换了3次(注意这个最前面,因为第一个移动过去的c是不会再变的,所以相当于现在第二位是最前面
把最近的a移动到最前面,发现a本来就在最前面,交换0次
把醉经的b移动到最前面,发现b也是在最前面,交换0次
最后一个a同理。

所以我们要做的就是找到当前的最近的相同字符,把它移动到最前面,交换次数就是这个字符前面有多少个字符。

我们可以用树状数组来模拟删除的过程。用个vector来记录每个字符出现的位置,由于位置是升序的,所以当前没用过的就是最前面的。用a数组来记录已经选到多少个了。
然后记录每个字符出现的位置就是序列自动机。(好高大上的名字

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i <  (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define _srep(n,m,i)for (register int i = (n); i >= (m); i--)
#define _sfor(n,m,i)for (register int i = (n); i >  (m); i--)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 2e5+5;
string s, t;
int c[N], n;
void upd(int pos, int va) {
  for(; pos <= n; pos += lowbit(pos)) c[pos] += va;
}
int qry(int pos) {
  int ans = 0;
  for(; pos > 0; pos -=lowbit(pos)) ans += c[pos];
  return ans; 
}
vector<int> ve[26];
int a[26];
int main() {
  cin >> n >> s; t = s;
  reverse(s.begin(), s.end());
  for(int i = 1; i <= n; i++) {
    ve[s[i-1]-'a'].push_back(i);
    upd(i, 1);
  }
  LL ans = 0;
  for(int i = 1; i <= n; i++) {
    int num = ve[t[i-1]-'a'][a[t[i-1]-'a']++];
    ans += qry(num) - 1;
    upd(num, -1);
  }
  cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值