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开始存就错,不知为何……