【tsinsen A1043】完美的代价(字符串处理+归并排序求逆序对)
A1043. 完美的代价
时间限制:1.0s 内存限制:512.0MB
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
否则输出Impossible
样例输入
5
mamad
mamad
样例输出
3
【题解】【字符串处理+归并排序求逆序对】
【这道题的做法是求出将整个串反转需要的最小步数,然后因为是求到回文串的步数,所以除以二即可】
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char s[8010];
int d[8010],n,nxt[8010],pre[8010],a[8010],a1[8010],ans=0;
bool p[8010],t=true;
inline void js(int f,int m,int l)
{
int i=f,j=m+1,k=f;
while (i<=m&&j<=l)
if(a[i]>a[j]) a1[k++]=a[j++],ans+=(m-i+1);
else a1[k++]=a[i++];
while(i<=m) a1[k++]=a[i++];
while(j<=l) a1[k++]=a[j++];
for(i=f;i<=l;++i) a[i]=a1[i];
return;
}
inline void gb(int f,int l)
{
if(f<l)
{
int m=(f+l)/2;
gb(f,m); gb(m+1,l);
js(f,m,l);
}
return;
}//归并排序求逆序对
int main()
{
int i;
scanf("%d",&n);
for(i=0;i<n;++i) cin>>s[i],d[s[i]-'a']++;//读入并记录每个字符出现了多少次
for(i=0;i<=25;++i)
if(d[i]&1==1)
if(!t) {printf("Impossible"); return 0;}
else t=false;//因为是回文字串,所以最多只能出现一个出现次数为奇数的字符
memset(d,-1,sizeof(d));
for(i=n-1;i>=0;--i) nxt[i]=d[s[i]-'a'],d[s[i]-'a']=i;//存每个字符的出现位置(当前位置存下一次出现的位置,倒着循环)
memset(d,-1,sizeof(d));
for(i=0;i<n;++i) pre[i]=d[s[i]-'a'],d[s[i]-'a']=i;//存每个字符出现的位置(当前位置存上一次出现的位置,正着循环)
memset(p,false,sizeof(p));
for(i=0;i<n;++i)
if(!p[i])
{
int u=i,v=d[s[i]-'a'];
while(v!=-1)
{
a[n-u-1]=v;
p[u]=true;
u=nxt[u]; v=pre[v];//指针后移(u代表的是翻转后的字串)
}
}//为每个位置的字符编号(按它完全倒置后的样子编),取最近的一个位置的当前字符(因为每次只能向左或向右移动一个单位,为了保证步数尽可能小)
gb(0,n-1);//归并排序求逆序对
printf("%d\n",ans/2);//因为是求到回文的步数,所以相当于只变一半
return 0;
}//从1开始存就错,不知为什么……
既然无能更改,又何必枉自寻烦忧