问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如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],但是这是找不到的情况
*/