先来考虑一下什么情况有解,如果出现次数为奇数的字符种类数大于等于2,就不行了,可以有一个字符出现奇数次,也可以所有字符都出现偶数次,这两种情况能变成回文串。
要是能确认最终回文串是什么样子的,就可以确定交换次数,因为确定了每个字符从哪个位置到哪个位置。具体地,对于一个相同的字符,他们之间的交换是没有意义的,所以原串中的第i个a,会去到回文串中第i个a的位置。记p[i]:原串中第i个字符(1-indexed,索引从1开始,为了方便)在回文串中的位置。则对p数组求逆序数,得到的就是最小相邻交换次数,这是一个关于相邻交换排序所需最少次数的结论。
对于某个字符a,其在回文串中对称的字符a在原串中唯一确定,例如a在原串中出现了6次,分别在位置i1,i2,i3,i4,i5,i6,则i1位置上的a在回文串中一定对应i6位置上的,同样,i2和i5配对,i3和i4配对。
所以可以将字符看成是成对出现的,则对于两个字符A,B,(B可能和A相等,比如他们都是'a')有以下相对位置的情况:
....A1...A2....B1....B2....(A1和A2最终配成一对,B1和B2最终配成一对,那么A1一定在A2前,B1一定在B2前)
此时最终要么交换成...A1...B1...B2....A2...,要么是.....B1....A1....A2.....B2.....,由于最终交换次数由逆序数决定,这两种情况的逆序数增量都是2,所以对结果的贡献一样。
....A1...B1....A2....B2.... 不论最终是...A1...B1...B2....A2..还是...B1...A1...A2....B2...,交换次数一样。
最后一种情况是: .....A1....B1....B2.....A2.... 此时应该让最终结果串中这些字符的相对顺序是:...A1....B1....B2...A2...这样不需要交换次数。
综合以上所有情况,有一个简单的结论,对于两个字符对,pair1和pair2,第一个字符对的字符出现位置是l1,r1,第二个是l2,r2,那么如果让l小的排在最终串的靠外部分,让l大的排在最终串的靠里部分。
对于A和B是相同的字符的情况呢,怎么排都可以,所以以上策略生效。
以下程序使用树状数组统计逆序对数量。
#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
#define lowbit(x) (x&(-x))
const ll maxn=2e5+5;
map<char,vector<ll>> appear;
struct ch_pair{
ll l,r;
char ch;
ch_pair(ll l=0,ll r=0,char ch='#'):l(l),r(r),ch(ch) {}
bool operator < (const ch_pair &rhs) const {
return l<rhs.l;
}
};
ll tree[maxn];
ll n;
void update(ll x,ll d){
while(x<=n){
tree[x]+=d;
x+=lowbit(x);
}
}
ll query(ll x){
ll sum=0;
while(x>0){
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
string s;
cin>>s;
//记录每个字符的出现位置
for(ll i=0;i<s.size();i++){
appear[s[i]].push_back(i);
}
//统计有多少个字符出现了奇数次
ll odd_num=0;
char odd_ch='#';
for(ll i='a';i<='z';i++){
if(appear[i].size()&1) {
odd_num++;
odd_ch=i;
}
}
if(odd_num>=2) cout<<-1<<"\n";
else {
vector<ch_pair> p;
//把出现奇数次的那个字符出现在中间的放到s1中间,其余当偶数次字符处理
vector<ll> tmp; //tmp记录出现奇数次的那个字符除了最中间的位置外出现的位置
for(ll i=0;i<appear[odd_ch].size();i++) {
if(i==appear[odd_ch].size()/2) continue;
tmp.push_back(appear[odd_ch][i]);
}
//将出现奇数次的字符改成出现偶数次的字符
appear[odd_ch].clear();
for(ll i=0;i<tmp.size();i++) appear[odd_ch].push_back(tmp[i]);
for(ll i='a';i<='z';i++){
for(ll j=0;j<=(ll)appear[i].size()/2-1;j++){
ll opp=(ll)appear[i].size()-j-1;
//把每个字符对放入p中
p.push_back(ch_pair(appear[i][j],appear[i][opp],i));
}
}
stable_sort(p.begin(),p.end());
/*for(ll i=0;i<p.size();i++){
printf("l=%lld r=%lld ch=%c\n",p[i].l,p[i].r,p[i].ch);
}*/
string s1;
n=s.size();
s1.resize(n+10); //s1:结果回文串
for(ll i=0;i<p.size();i++){
s1[i]=s1[n-i-1]=p[i].ch;
}
//别忘了出现次数为奇数的那个特殊字符
if(odd_ch!='#'){
ll center=n/2;
s1[center]=odd_ch;
}
/*cout<<s1<<"\n";
printf("Runs to HERE\n");*/
//appear:每个字符的原出现位置
//appear1:结果串中的新位置
ll pos[maxn];
appear.clear();
for(ll i=0;i<n;i++) appear[s[i]].push_back(i);
map<char,vector<ll>> appear1;
for(ll i=0;i<n;i++) appear1[s1[i]].push_back(i);
for(ll i='a';i<='z';i++){
for(ll j=0;j<(ll)appear[i].size();j++){
pos[appear[i][j]+1]=appear1[i][j]+1;
}
}
/*for(ll i=1;i<=n;i++) cout<<pos[i]<<" ";
cout<<"\n";*/
//统计逆序数
ll ans=0;
for(ll i=n;i>=1;i--){
ans+=query(pos[i]);
update(pos[i],1);
}
cout<<ans<<"\n";
}
return 0;
}