问题描述:
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式:
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式:
如果可能,输出最少的交换次数。
否则输出Impossible
编程实现:
(解法来自:https://blog.youkuaiyun.com/liuchuo/article/details/51990430)
#include <iostream>
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的判断)
补充:
关于贪心:
贪心法是求解一类问题最优化的方法,它总是考虑在当前状态下局部最优(或较优)的策略,来使全局的结果达到最优或较优的状态。
显然,如果采取较优而非最优策略,得到的全局结果也无法是最优的。而要获得最优结果,则要求中间的每步策略则是最优的。因此
严谨采用贪心法来解决最优化问题需要对采取的策略进行证明。证明的一般思路时采取反证法或数学归纳法,即假设策略不能导致
最优解,然后通过一系列推导来得到矛盾,以此证明策略是最优的,最后使用数学归纳法保证全局最优。 (算法笔记)