tsinsen A1043. 完美的代价

35 篇文章 0 订阅
24 篇文章 0 订阅
A1043. 完美的代价
时间限制: 1.0s   内存限制: 512.0MB  
总提交次数: 1985   AC次数: 446   平均分: 44.86
问题描述
  回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
  交换的定义是:交换两个相邻的字符
  例如mamad
  第一次交换 ad : mamda
  第二次交换 md : madma
  第三次交换 ma : madam (回文!完美!)
输入格式
  第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
  第二行是一个字符串,长度为N.只包含小写字母
输出格式
  如果可能,输出最少的交换次数。
  否则输出Impossible
样例输入
5
mamad
样例输出
3


题解:先从简单的说起,如何判断 Impossible,如果长度为偶数,那么所有出现过的字符的个数必须都是偶数,这样才能保证一一对应;如果是奇数,那么允许有一个字符的个数是奇数。如果上述条件不满足,直接输出Impossible就可以了。
如何求最少的交换次数呢?首先先把字符串翻转,然后让原串与翻转串相同的字符一一对应,例如mamad ,翻转后为damam,但是得到的数列不是54321,而应该是52143,也就是说原字符串中某个字符第一次对应翻转字符串的第一次出现,得到数列后求一下该数列的逆序对数除以2即为答案。
为什么呢?因为把原串转成翻转串的过程中必经过是回文串的时刻。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int a[8100],b[8100],n,m,ans,ch[30][8100],p[8100],r[8100];
int id[30];
void pd()
{
  if (n%2==0)
   {
   	 for (int i=1;i<=26;i++)
   	  if (id[i]%2!=0)
   	   {
   	   	 printf("Impossible\n");
   	   	 exit(0);
   	   }
   }
  else
   {
   	 int k=0;
   	 for (int i=1;i<=26;i++)
   	  if (id[i]%2!=0)
   	   k++;
   	 if (k>1)
   	  {
   	  	 printf("Impossible\n");
   	   	 exit(0);
   	  }
   }
}
void mergesort(int s,int t)
{
	if (s==t) return;
	int mid=(s+t)/2;
	mergesort(s,mid); mergesort(mid+1,t);
	int i=s; int j=mid+1; int mp=s;
	int k[8100]; memset(k,0,sizeof(k));
	while (i<=mid&&j<=t)
	 {
	   if (r[i]<r[j])
	    k[mp]=r[i],i++,mp++;
	   else
	    k[mp]=r[j],ans+=mid-i+1,j++,mp++;
	 }
	for (int l=i;l<=mid;l++)
	 k[mp]=r[l],mp++;
	for (int l=j;l<=t;l++)
	 k[mp]=r[l],mp++;
	for (int l=s;l<=t;l++)
	 r[l]=k[l];
}
int main()
{
  memset(id,0,sizeof(id));
  scanf("%d\n",&n);
  for (int i=1;i<=n;i++)
   {
   	 char c; c=getchar();
   	 int cc=c-96;
   	 id[cc]++;  ch[cc][0]++;
   	 ch[cc][ch[cc][0]]=i;
   	 a[i]=cc; b[n-i+1]=cc;
   }
  pd();
  for (int i=1;i<=n;i++)
   {
   	 p[b[i]]++;
   	 r[i]=ch[b[i]][p[b[i]]];
   }
  /*for (int i=1;i<=n;i++)
   printf("%d ",r[i]);
  cout<<endl;*/
  mergesort(1,n);
  printf("%d",ans/2);
  return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值