刷题——蓝桥杯 BASIC-19 完美的代价

问题描述:

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

输入格式:

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

输出格式:

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

编程实现:

(解法来自:https://blog.csdn.net/liuchuo/article/details/51990430)

#include 
using namespace std;
int main() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    int j = n - 1;
    int cnt = 0;//cnt用来统计交换的次数
    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) {//对于s[i]如果找不到相同的字符 
                if(n % 2 == 0 || flag == 1) {//impossible的两种情况,即找不到相同字符且n为偶数或已经存在奇数次字符 
                    cout << "Impossible";
                    return 0;
                }
                flag = 1;
                cnt += n / 2 - i;//该字符交换到适宜位置需要的次数 
            } else if(s[k] == s[i]) {//第k个位置上存在和s[i]相同的字符 
                for(int l = k; l < j; l++) {// ? 
                    swap(s[l], s[l+1]);//使用swap函数(在命名空间std中)把s[k]换到s[j]处
                    cnt++;//统计交换次数
                }
                j--;//对应字符完成归位,匹配范围内缩 
                break;
            }
        }
    }
    cout << cnt;
    return 0;
}

解析:

这个解法中,字符串从头开始,固定左侧的每一个可成对的字符,将对应字符移动到右侧的相应位置。
若字符串为奇数个字符,则其中应有一个字符没有"配偶",这个字符应当置于中心。但若直接将之交换并记录次数
会使计数比最小的交换次数多1,比如 bdaabcc 这个字符串,若交换了d则在归位a时会产生额外的一次交换
因此对于这个字符不进行直接的交换操作,而是默认在完全归位后对其进行交换
而此时也同样不必进行交换累计,直接根据其所在位置使用公式来进行计算交换次数累计到cnt中即可
(由于对实际累计没有影响,所以在过程中就进行计算,等效于完全归位后计算,也便于impossible的判断)

补充:

关于贪心:
贪心法是求解一类问题最优化的方法,它总是考虑在当前状态下局部最优(或较优)的策略,来使全局的结果达到最优或较优的状态。
显然,如果采取较优而非最优策略,得到的全局结果也无法是最优的。而要获得最优结果,则要求中间的每步策略则是最优的。因此
严谨采用贪心法来解决最优化问题需要对采取的策略进行证明。证明的一般思路时采取反证法或数学归纳法,即假设策略不能导致
最优解,然后通过一系列推导来得到矛盾,以此证明策略是最优的,最后使用数学归纳法保证全局最优。 (算法笔记)

你可能感兴趣的:(刷题(C/C++))