本文参考:https://blog.csdn.net/qq_40605470/article/details/79268979
这篇文章写的很仔细,看不懂的,可以参考该文。
问题描述
基础练习 完美的代价
时间限制:1.0s 内存限制:512.0MB
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输入
5
mamad
样例输出
3
思路分析:
1.先判断输入的字串能否构成回文
2.若不能构成回文,则输出;若能构成回文,则计算最少的交换次数。
-
算法 :判断能否构成回文
若字符串长度为偶数,若存在奇数个数的字符,则不能构成回文; 若字符串长度为奇数,若存在两个以上奇数个数的字符,则不能构成回文; 综上考虑:若字符长度为偶数,且存在奇数个数的字符,那么奇数个数的字符一定有两个以上。 所以判断能否构成回文,直接判断奇数个数字符总数超过两个即可。
-
算法:计算交换次数
交换 :只能和相邻的字符交换 交换次数最少:每个字符都只能向一个方向移动,从外部向内部移动(保证内部改变不影响外部改变),而且只需要移动一半即可。
具体过程
- 以abbchca字串为例,交换次数为3次。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
a | b | b | c | h | c | a |
- 从左边i=0开始,为a,然后从右边第一个往左查找,右边第一个为a,形成回文;
- 在左边i=1开始,为b,然后从右边第二个往左查找,第一个与b相等的字符,标记该位置为p=2,然后从p开始往右边第二个位置j=5依次进行交换,并统计次数。
- 交换过程如下
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
a | b | b | c | h | c | a |
a | b | c | b | h | c | a |
a | b | c | h | b | c | a |
a | b | c | h | c | b | a |
-
一直重复此过程,直到字符串一半的位置,或者已经构成回文结束
-
另还有一种情况,若存在找不到相等字符的情况;例如:hbabcca,交换次数为6次。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
h | b | a | b | c | c | a |
- 从左边i=0,为h,找不到相等的字符,直接计算h到中间位置的移动次数,但不用交换;
- 然后从左边i=1,为b,然后从右边j=6的位置开始向左查找相等字符,然后记住该位置为p=3,然后从p=3位置往j=6的位置进行交换字符,并计算交换次数。
- 交换过程
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
h | b | a | b | c | c | a |
h | b | a | c | b | c | a |
h | b | a | c | c | b | a |
h | b | a | c | c | a | b |
- 一直重复此过程,直到字符串一半的位置,或者已经构成回文结束。
代码实现
#include<iostream>
#include<cstring>
using namespace std;
void input();//输入
bool Judje_huiwen(char a[]);//判断是否为回文
void Judje();//判断能否构成回文
void move();//计算移动的次数
int num[26]={0};//总计字母出现的次数
char a[8000];//输入字串的长度
int N;//输入的长度
int main(){
input();
Judje();
}
void input(){
cin>>N;
for(int i=0;i<N;i++){
cin>>a[i];
}
}
bool Judje_huiwen(char a[]){
char b[N];
for(int i=N-1,j=0;i>=0;i--,j++){
b[j]=a[i];
}
if(strcmp(a,b)==0){
return true;
}
else{
return false;
}
}
void Judje(){
int sum_odd=0;//字母奇数的个数
for(int i=0;i<N;i++){//统计每个字符的个数
int temp=a[i];
num[temp-97]++;
}
for(int i=0;i<26;i++){
if(num[i]%2==1){
sum_odd++;
}
}
if(sum_odd>=2){//两个以上字母的个数为奇数
cout<<"Impossible"<<endl;
}
else{
move();
}
}
void move(){
int count=0;//移动的次数
int move=0;//最后调整的次数
if(Judje_huiwen(a)){//已经是回文
cout<<"0"<<endl;
}
else{
int i,j,k;
int len=N;//len为从右往左开始查找字符匹配的起始位置
int l=(N+1)/2;
for(i=0;i<l;i++){
for(j=len-1;j>i;j--){
if(a[i]==a[j]){
//交换
for(k=j;k<len-1;k++){
a[k]=a[k+1];
count++;
}
a[len-1]=a[i];
len--;//找到匹配一次,就从右往左移动一次
break;
}
}
if(i==j){//表示该字符未找到匹配的
move=l-i-1;
}
}
}
cout<<move+count<<endl;
}