问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输入
5
mamad
样例输出
3
思路
emmm~~~第一眼看到这个题目觉得真的难,后来题目提示了贪心算法才懂,不要去想哪个排在第一个,哪个排在第二个,按题目给的来,举个例子,amdam
,第一个字符为a
,我就把这个字符当作第一个,然后在从后往前找a
,找到了相同之后,相邻两个字符移动,将找到的字符移到第n-1
位,然后将尾部减一,便于下次移到第n-2
位,如果没找到的话,分为两种情况,
1.n%2==0
,没找到直接return
。
2.n%2==1
,没找到就计算把这个字符与最中间字符交换所用的次数,这里可能有个疑问了,为什么不直接交换呢?我们假设一个是回文串的字串为ambcbca
,当检测到m
的时候,count=2
,当遍历到它的第四个字符c
时,l=3
,跳出循环,但此时第四个字符并没有移动,所以前面直接加上的步数就是将第四个字符c
与第二个字符将换位置所需要的步数。
AC代码
#include<iostream>
#include<string>
using namespace std;
int main()
{
int i, j, k, l, n, count = 0, flag = 0;
string a;
cin >> n >> a;
l = n - 1;
for (i = 0; i < l; i++)
{
for (j = l; j >= i; j--)
{
if (i == j)
{
if (n % 2 == 0 || flag == 1)
{
cout << "Impossible";
return 0;
}
flag=1;
count += n / 2 - i;
}
else if (a[i] == a[j])
{
for (k = j; k < l; k++)
{
swap(a[k], a[k + 1]);
count++;
}
l--;
break;
}
}
}
cout << count;
}
5月24号,重写。AC代码
#include<iostream>
using namespace std;
int flag=0;
int main()
{
int i,j,n,ans=0;
string a;
cin>>n>>a;
for (i=0;i<n;i++)
{
for (j=n-1;j>=i;j--) //必须要j==i
{
if (i==j) //没找到能和a[i]对称的字符
{
if (a.size()%2==0||flag==1)
{
cout<<"Impossible";
return 0;
}
flag++;
ans+=a.size()/2-i;
}
else if (a[i]==a[j]) //找到了
{
for (int k=j;k<n-1;k++)
swap(a[k],a[k+1]);
ans+=n-1-j;
n--;
//此时第一个和最后一个字符对称
break;
//该字符已对称,后面的循环不再考虑最后一个字符
}
}
}
cout<<ans;
}