试题 基础练习 完美的代价

资源限制
时间限制:1.0s 内存限制:512.0MB


问题描述
  回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
  交换的定义是:交换两个相邻的字符
  例如mamad
  第一次交换 ad : mamda
  第二次交换 md : madma
  第三次交换 ma : madam (回文!完美!)


输入格式
  第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
  第二行是一个字符串,长度为N.只包含小写字母
输出格式
  如果可能,输出最少的交换次数。
  否则输出Impossible


样例输入
5
mamad
样例输出
3


思路解析:

  • 首先要判断是否满足回文,即没有“对象”的字符的个数要小于等于一

我在判断的时候使用了两种容器:set和multiset容器。

s.begin()       //  返回指向第一个元素的迭代器
s.clear()       //  清除所有元素
s.count()       //  返回某个值元素的个数
s.empty()       //  如果集合为空,返回true(真)
s.end()         //  返回指向最后一个元素之后的迭代器,不是最后一个元素
s.equal_range() //  返回集合中与给定值相等的上下限的两个迭代器
s.erase()       //  删除集合中的元素
s.find()        //  返回一个指向被查找到元素的迭代器
s.get_allocator()   //  返回集合的分配器
s.insert()      //  在集合中插入元素
s.lower_bound() //  返回指向大于(或等于)某值的第一个元素的迭代器
s.key_comp()    //  返回一个用于元素间值比较的函数
s.max_size()    //  返回集合能容纳的元素的最大限值
s.rbegin()      //  返回指向集合中最后一个元素的反向迭代器
s.rend()        //  返回指向集合中第一个元素的反向迭代器
s.size()        //  集合中元素的数目
s.swap()        //  交换两个集合变量
s.upper_bound() //  返回大于某个值元素的迭代器
s.value_comp()  //  返回一个用于比较元素间的值的函数

转载于:
(https://blog.csdn.net/love20165104027/article/details/81510406)

  • 还要满足交换次数最少(贪心,此处不做详解),即当遇到没有“对象”的字符时,不需要真正的去把它交换到中间,只需要计算出该过程中需要的交换次数即可,加到count上即可。

代码如下:

#include<iostream>
#include<string>
#include<set>
using namespace std;
string s;//全局变量,获取用户输入的字符串
bool judge(int n)
{
   multiset<char>m1;//两种容器,set容器不能出现重复的,multiset容器可以出现重复值,
   set<char>m2;//都是定义的char类型的,不能定义成string型的,要将s中的字母以单个字符的形式存入容器。
   int flag = 0;//单个奇数字符的个数。
   for (int i = 0; i < n; i++)//利用循环将s中的单个字符存入容器中。
   {
   	m1.insert(s[i]);
   	m2.insert(s[i]);
   }
   multiset<char>::iterator p1 = m1.begin();
   set<char>::iterator p2 = m2.begin();
   for (p2; p2 != m2.end(); p2++)
   {
   	if (m1.count(*p2) % 2 != 0)//count的作用是返回*p2这个字符的个数。
   		flag++;//若是奇数个flag++。
   }
   if (flag > 1)//当大于一个时肯定不能构成回文字符串!
   	return false;
   else
   	return true;	
}
void show(int x)
{
   if (!judge(x))
   	cout << "Impossible" << endl;
   if(judge(x))
   {
   	int count = 0;//交换的次数。
   	int end = x-1;//字符串s的末尾。
   	int i, j;
   	for (i = 0; i < end; i++)//头开始
   	{
   		for (j = end; j > i; j--)//尾开始
   		{
   			if (s[i] == s[j])
   			{
   				for (int k = j; k <= end-1; k++)
   				{
   					swap(s[k], s[k + 1]);//将找到的对应的字符交换到尾巴。
   					count++;//次数对应增加。
   				}
   				end--;//交换完毕后,end--,就是尾巴向前一位
   				break;//此时跳出第二个循环,结果就是头和尾巴都向着中间移了一位。
   			}
   		}
   		if (i == j)//此时就是代表着那个没有“对象”的那个字符。
   		{
   			count = count + (x - 1) / 2 - i;//算出他移到中间位置的交换次数,但此时并不需要真正的进行交换,
   		}//显然此处就是单纯的是count增加了,并没有使用swap进行交换,目的就是为了满足最小次数。
   	}
   	cout<<count<<endl;
   }
}
int main()
{
   int n;
   cin >> n;
   cin >> s;
   show(n);
}

评测数据(可自行验证):

  • 58
    fkgikmvqufokkuuiwqbfugwwwukigsvobbsygkokyuwgbmuqgkoqfjuiwj
  • 214

蓝桥杯官网评测结果:

在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值