问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
否则输出Impossible
样例输入
5
mamad
mamad
样例输出
3
解题思路:
求最少的交换次数,考虑用贪心算法,当一个字符不动,另一个字符移动到对称位置时移动步数最少。
有字符个数为奇数和偶数两种情况:
- 字符个数为偶数,不能有不对称的字符。
- 字符个数为奇数,没有对称字符的字符只能有一个,设变量flag来标记是不是第一次不对称。
另外需要注意:
奇数情况下若找到不对称字符,不能先移动它,将移动至中间所需要的步数加入总步数即可,可以想象第一个字符为非对称字符的情况代入程序中,非对称字符移动到中间,使得第二个字符变成第一个字符,但是外循环已经跳过了第一个字符(如果要先移动非对称字符,则移动后需要从上一个位置重新计算)。
#include <iostream> #include <cstdio> using namespace std; int main() { char str[8001]; int n, end, j, k, cnt=0, flag=1; cin>>n;getchar(); for(int i=0; i<n; i++) scanf("%c", &str[i]); end = n-1; //end为最后一个字符下标 //从头开始遍历 for(int i=0; i<end; i++) { //从末尾开始遍历 for(j=end; j>=i; j--) { //找不到相同的字符 if(i == j) { //判断不是第一次找不到匹配的字符 或 n为偶数 if(flag==0 || n%2==0) { //返回impossible printf("Impossible\n"); return 0; } //是第一次找不到且n为奇数,则将flag变为0,计算应该移动多少位(n/2-j)但不要真的移动 /* *不匹配的字符如果在数组的后半部分则当其他位置匹配时必然n/2==j, *即不需要移动,如果在前半部分则需要移动n/2-j,当然减i也行(此时i==j) */ else { flag = 0; cnt += n/2-i; } } //找到相同的字符 else if(str[j] == str[i]) { //将该位置的字符与向后相邻的字符交换,直到end for(k=j; k<end; k++)//注意不能连end也算入,k+1已经替换了end { swap(str[k], str[k+1]); cnt++; } //end向前移动一位(因为该位置已经对称) end--; break; } } } cout<<cnt<<endl; return 0; } /*注: 程序中的判断是否找到相同字符if... else if...位置不可交换,因为i==j时, 必然str[i]==str[j],但是这是找不到的情况 */