题目
基础练习 完美的代价
时间限制:1.0s 内存限制:512.0MB
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输入
5
mamad
样例输出
3
思路
构造回文串,O(n^2)方法
不能构成回文的情况:( 代码体现在 if (n % 2 == 0 || single) 这一行 )
1.字符串是偶数,并且有一个奇数的字符
2.字符串中有俩个或以上的奇数的字符
能构成回文的情况:
1.字符串是奇数时,从第一个字符开始循环,从最后一个字符开始往前寻找
如果找到一样的字符,则将找到的字符移到最后一个,并且将其标记为已处理(或者说是,需处理的区间减少,代码是end–)
如果未找到,则说明此字符是奇数字符,奇数字符直接统计移动到中间需要多少步就行,因为该字符实际上应该是最后才
移动,若先移动,则在部分情况下会导致步数增加(如assmm字符串)
接下来从第二个字符开始循环,从未处理的最后一个字符开始往前寻找,以此类推
(因为俩边的字符已经处理过,无需再处理)
2.字符串的偶数时,即不纯在奇数字符的情况,
直接从第一个字符开始循环,从最后一个字符开始往前寻找
如果找到一样的字符,则将找到的字符移到最后一个,并且将其标记为已处理
然后继续循环即可
代码
#include<iostream>
using namespace std;
int main()
{
int n, cnt = 0;//cnt指步数
bool single = false;//记录是否存在奇数字符
string s;
cin >> n >> s;
int end = n - 1;//end指未排序的最后一个字符,初始值就是第n-1个字符
for (int i = 0; i <= n/2; i++)
{
for (int j = end; j >= i; j--)
{
//打印详细步骤,调试用
//for (int m = 0; m < n; m++)
// cout << s[m];
//cout << " i :" << i << "j :" << j << endl;
if (j == i)
{
//当j==i时,说明已经发现一个单独的字符,如果数列为偶数列,或者之前也存在一个单字符,那么就不能构成回文串
if (n % 2 == 0 || single)
{
cout << "Impossible";
return 0;
}
single = true;//标记已经发现单个字符
cnt += n / 2 - j;//累计将他移动到中心需要的步数
break;
}
else if (s[j] == s[i])//发现与当前字符相等的字符
{
//将较后的字符移到未处理的最后一位,即end的位置
for (int k = j; k < end; k++)
{
swap(s[k], s[k + 1]);
cnt++;
}
end--;//第end的位置,已处理完毕,不能再处理,end--
break;
}
}
}
cout << cnt;
return 0;
}