给定一个字符串 ,你每次可以交换 的相邻两个字符,问最少进行多少次交换才能使得 变为回文串。数据保证一定能够在有限次交换操作后使得 变为回文串。
。
设 表示最后第 个位置的字符原来是哪个位置上的,显然答案就是 的逆序对个数,并且确定了 就可以确定 。
这里有个结论: 一定是递增的。
证明:
若存在 满足 ,则我们可以交换 和 ,此时逆序对个数一定会减 ,相应的我们也要交换 和 ,这会让逆序对个数加 或者减 ,所以这样调整一定是不劣的。
那么乱贪就行了,代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <deque>
using namespace std;
const int S=1000005;
int n;
char a[S];
deque<int> q[30];
int c[S],res[S];
inline void add(int x,int val)
{
for(int i=x;i<=n;i+=i&-i) c[i]+=val;
}
inline int que(int x)
{
int res=0;
for(int i=x;i>=1;i-=i&-i) res+=c[i];
return res;
}
int main()
{
scanf("%s",a+1);
n=strlen(a+1);
for(int i=1;i<=n;i++) a[i]=a[i]-'a'+1;
for(int i=1;i<=n;i++) q[a[i]].push_back(i);
for(int i=1;i<=n/2;i++)
{
int mnval=1e8,idx=0;
for(int j=1;j<=26;j++)
{
if(q[j].size()>1)
{
int pre=abs(q[j].front()-i);
if(pre<mnval) mnval=pre,idx=j;
}
}
res[i]=q[idx].front();
res[n-i+1]=q[idx].back();
q[idx].pop_front();
q[idx].pop_back();
}
if(n&1) for(int j=1;j<=26;j++) if(q[j].size()>=1) res[n/2+1]=q[j].front();
long long ans=0;
for(int i=n;i>=1;i--)
{
ans+=que(res[i]);
add(res[i],1);
}
printf("%lld\n",ans);
return 0;
}