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

基础练习 完美的代价


问题:

问题描述

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

输入格式

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

输出格式

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

样例输入

5
mamad

样例输出

3


需要了解:
这道题的关键字是贪心算法,去百度了一下贪心算法,简单来说就是在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

在本题中也就是找到能够与之相匹配的字符。然后,进行交换,直至达到目标要求。

题目分析:


  • 首先分析Impossible 的两种情况
    • n为奇数时,如果已经有一个字符出现的次数为奇数,还找到了一个字符出现的次数为奇数,那么就不能构成回文串。
    • n为偶数时,只要找到有一个字符出现的次数为奇数,那么就不能构成回文串。
  • 还有一个就是题目所说的最小的交换次数
    • 如果 n 为偶数,那么从第一字符开始,从后往前找第一个和它相同的字符,如果找了,就将找到的字符交换到最后一个位置,在下一次遍历时,就可以不用管刚才已经交换好的那来两个字符,下一次从第二个字符开始,从倒数第二个字符开始遍历,执行和上述相同的操作。
    • 如果 n 为奇数,在字符串的某一个位置找到了那个出现次数为奇数的字符,我们不必将次字符现在就交换到中间位置,而是先计算它到中间位置需要交换的次数,然后累加到 count 中,将剩下的字符都交换到对称后,再交换这个字符即可。

这是因为如果把奇数移动到中间,假设有一对数都在以中间为界限的左边或者都在右边的话,那么交换成回文的时候就一定要经过中间,这就会造成count多增加了一次,这是不必要的,因为可以所有的回文移动完了之后再把这个独立的奇数移动过去,不仅能达到同样的效果还能保证交换次数最少。
例如:
有这么几个数

b d c c f f b

我们不考虑第二个奇数直接交换第三个数,这样只需要1步,但是当你把第二个奇数交换到中间之后就会有如下结果

b c c d f f b

可以了解一下 φ(>ω<*)

代码:

#include <iostream>
#include <string>
using namespace std;
int main() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    int end = n - 1;//字符串最后一个字符 
    int count = 0;//交换次数 
    int OddNum = 0;//判断是否已经有一个单独的奇个数的字符了
    for (int i = 0; i < end; ++i) {//从第一个字符到倒数第二个字符遍历 
        for (int j = end; j >= i; --j) {//从最后一个开始,到第i个字符,寻找与s[i]相同的字符
            if (i == j) {//如果没找到 
                if (n % 2 == 0 || OddNum == 1) { //不可能的两种情况 
                    cout << "Impossible" << endl;
                    return 0;
                }
                OddNum = 1;//找到一个字符出现的次数为奇数 
                count += n / 2 - i;//将次字符交换到中间位置的次数 
            }
            else if (s[i] == s[j]) {//如果找到了,将s[j]交换到s[end]位置 
                for (int k = j; k < end; ++k) {
                    swap(s[k], s[k + 1]);//交换相邻两个位置的字符 
                    ++count;
                }
                --end;//末尾递减 
                break;  //开始从i+1处重复操作 
            }   
        }
    }
    cout << count << endl;
    system("pause");
    return 0;
}

对于我:
但是在这里我还发现我有一个基础知识点薄弱的地方那就是多个if跟if··· if else 的区别

if (条件){语句}

这种格式中,程序会依次判断条件1和条件2是否成立并根据结果决定是否执行语句1和语句2,也就是说,第一个 if 块和第二个 if 块没有影响(除非在执行第一个 if 块的时候就凶残地 return 了)

而下面这种格式,

if (条件){}
else if(条件){}

f 块和 else if 块本质上是互斥的!也就是说,一旦语句1得到了执行,程序会跳过 else if 块,else if 块中的判断语句以及语句2一定会被跳过;同时语句2的执行也暗含了条件1判断失败和语句1没有执行;当然还有第3个情况,就是条件1和条件2都判断失败,语句1和语句2都没有得到执行。

原文地址:https://www.cnblogs.com/neverth/p/11760943.html