小前置知识
前置题目
这里简单的讲解一下:答案为什么是逆序对的个数,因为我们每次只能交换相邻的元素,对于我们的每次操作,我们可以将我们整个序列的逆序对加一或者减一,因为我们最终序列的逆序对数为0,所以我们每次操作一定是将逆对数减一,这样我们逆序对数的个数就是答案的个数。
大前置知识
传送门
题目含义: 给你一个字符串,每次只能交换相邻的元素,问将整个字符串翻转的最小步数是多少。
题解:首先我们先注意到只能交换相邻的元素,这时候我们就应该开始往逆序对的方面上想,我们可以发现这道题目与前边的有些许不同,首先对于原串来说,位置
I
I
I上的字母在翻转后其不一定是在他的对称位置上,这时候我们就想如果其不在它的对称位置上,会是在哪些位置上呢,不如我们将相同的字母全部都取出来,单个来分析这个题,假设我们原串中字母a的位置我们已经记录下来了,翻转后的串中字母a 的位置我们班也记录下来,我们只需要将原串中字母a的位置与翻转后的串中字母a的位置进行匹配不就好了?那我们只需要按照原串和转化后的串中某个字母出现的按照大小排序,再一一进行配对就好了。
求逆序对可以用树状数组或者归并排序。
代码
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int tr[N], n;
void add (int x, int c)
{
for (int i = x; i <= n; i += i & -i) tr[i] += c;
}
int sum (int x)
{
int res = 0;
for (int i = x; i; i -= i & -i) res += tr[i];
return res;
}
int main()
{
cin >> n;
string str; cin >> str;
vector <int> arr[30];
for (int i = 0; i < n; i ++) arr[str[i] - 'a'].push_back (i + 1);
static int pos[N];
LL res = 0;
for (int i = n - 1; i >= 0; i --)
{
int t = n - 1 - i;
pos[i] = arr[str[t] - 'a'].back();
arr[str[t] - 'a'].pop_back();
}
for (int i = n - 1; i >= 0; i --) res += sum (pos[i]), add (pos[i], 1);
cout << res << endl;
return 0;
}
正文
题意:给你一个只有四个字母组成的一个字符串,你可以将这个字符串乱序,问使将乱序字符串还原的最小操作数的最大值是哪个字符串,操作的限制是只能交换相邻的元素。
先说结论,最佳答案一定是所有相同的字母在一块,由于只是4个字母,所以我们可以枚举全排列,只不过是24种情况,然后每次判断每种情况是O(nlogn)的,但是!鉴于这道题目字母数量只有4个,并且结构较为单一。 那么我们还有一个O(n) 的求逆序对的方法, 就是预先求出每两个不同字母之间相对关系所产生的逆序对的数量,这样我们就可以O(1)的复杂度求出来, 预处理的复杂度是O(n) , 鉴于题目给的时间是2s所以哪种方式都可以。
证明先咕咕
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL tr[N];
string str;
int n;
void add (int x, int c)
{
for (int i = x; i <= n; i += i & -i) tr[i] += c;
}
int s(int x)
{
int res = 0;
for (int i = x; i; i -= i & -i) res += tr[i];
return res;
}
int a[4];
char p[] = {'A', 'N', 'T', 'O'};
LL get (vector <int> temp)
{
vector <int> arr[4];
for (int i = 0; i < n; i ++)
{
if (str[i] == 'A') arr[0].push_back (i + 1);
else if (str[i] == 'N') arr[1].push_back (i + 1);
else if (str[i] == 'T') arr[2].push_back (i + 1);
else arr[3].push_back (i + 1);
}
static int pos[N];
static int sum[5];
sum[0] = a[temp[0]];
for (int i = 1; i < 4; i ++) sum[i] = sum[i - 1] + a[temp[i]];
for (int i = sum[0]; i >= 1; i --)
{
pos[i] = arr[temp[0]].back ();
arr[temp[0]].pop_back();
}
for (int i = sum[1]; i > sum[0]; i --)
{
pos[i] = arr[temp[1]].back ();
arr[temp[1]].pop_back();
}
for (int i = sum[2]; i > sum[1]; i --)
pos[i] = arr[temp[2]].back (), arr[temp[2]].pop_back();
for (int i = sum[3]; i > sum[2]; i --)
pos[i] = arr[temp[3]].back (), arr[temp[3]].pop_back();
LL res = 0;
for (int i = n; i >= 1; i --) res += s (pos[i]), add (pos[i], 1);
for (int i = 1; i <= n; i ++) add(pos[i], -1), pos[i] = 0;
return res;
}
int main()
{
int T; cin >> T;
while (T --)
{
vector <int> t = {0, 1, 2, 3};
LL Max = -1;
cin >> str;
n = str.size();
a[0] = a[1] = a[2] = a[3] = 0;
for (int i = 0; i < str.size(); i ++)
if (str[i] == 'A') a[0] ++;
else if (str[i] == 'N') a[1] ++;
else if (str[i] == 'T') a[2] ++;
else a[3] ++;
vector <int> res;
do
{
LL g = get (t);
if (Max < g) Max = g, res = t;
}while (next_permutation(t.begin(), t.end())) ;
for (int i = 0; i < 4; i ++)
{
for (int j = 1; j <= a[res[i]]; j ++) putchar (p[res[i]]);
}
puts ("");
}
return 0;
}