蓝桥杯-完美的代价(C++详细解释版)

题目

题目描述

回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。

交换的定义是:交换两个相邻的字符例如mamad

第一次交换 ad : mamda

第二次交换 md : madma

第三次交换 ma : madam (回文!完美!)

输入

第一行是一个整数N,表示接下来的字符串的长度(N < = 8000) 第二行是一个字符串,长度为N.只包含小写字母

输出

如果可能,输出最少的交换次数。 否则输出Impossible

样例输入

5 mamad

样例输出

3

思路讲解

首先是Impossible的情况

1️⃣当N是偶数且有一个字符出现次数为奇数时那一定不可能构成回文数如adaaaa

2️⃣当N是奇数且有两个不等的字符出现次数为奇数时页一定不可能构成回文数如abada

然后是可以移成回文数的情况

采用贪心思想,从左向右遍历当前字符串s[i],然后从右向左遍历找到与当前字符串s[i]相等的字符串s[k],将s[k]移到字符串末尾(彼此相邻的移动),然后将指向末尾的指针减一。

再看s[i+1],从右(末尾已经减一)向左遍历找到与其相同的字符再将其移到末尾,再将末尾减一,以此类推

🌰举个例子

比如mamad,首先从左到右s[0]即m,然后从右向左遍历找到与其相同的字符即s[2]

然后将s[2]移到字符末尾即s[4],移动的过程是s[2]和s[3]交换然后s[3]再和s[4]交换,然后将末尾指针减一,此时字符串变成了maadm

接着是s[1]即a从右向左遍历找到了s[2]与其相等,然后将其移到末尾,注意此时末尾指针已经减一,也就是s[2]移到s[3]的位置,此时字符串变成了madam

此时从左到右遍历的指针和从右向左的指针相等程序结束

整个过程一共移动了3次而3就是答案

可以发现整个移动过程其实和题目说的移动过程并不一致

完整代码

#include<bits/stdc++.h>
using namespace std;
int n;
string s;

int main() {
    cin >> n;
    cin >> s;

    int j = n-1;
    int res = 0;	//res用来统计交换的次数
    int flag = 0;	//flag用来统计出现奇数次数的字符个数

    for(int i = 0; i < j; i++)	//i指针从头遍历到倒数第二个字符
	{
        for(int k = j; k >= i; k--)	//k指针从后面往前一直到i寻找和s[i]相同的s[k]
		{
            if(k == i)	//如果找不到相同的
			{
				flag++;
                if(n % 2 == 0 || flag == 2)	//impossible的两种情况
				{
                    cout << "Impossible"<<endl;
                    return 0;
                }               
                res += n / 2 - i;	//n为奇数时唯一一个奇数次出现的字符移到中间的次数
									//n/2-i一定大于0即这个数是在整体的左边位置,如果在右边遍历前面的i时就已经把它移到中间了,如aaaad  
            } 
			else if(s[i] == s[k])   
			{
                for(int l = k; l < j; l++) 
				{
                    swap(s[l], s[l+1]);//把s[k]换到s[j]处  
                    res++;//统计交换次数
                }
                j--;	//将一个字符调到末尾后j减一即将末尾指针往前移一位
						//方便下次交换到末尾且i和j相等时此时已经是回文数第一个for循环就结束了
                break;
            }
        }
    }
    cout << res <<endl;
    return 0;
}

参考

蓝桥杯 BASIC-19 基础练习 完美的代价
BASIC-19 完美的代价

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值