蓝桥杯 基础练习 完美的代价
资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输入
5
mamad
样例输出
3
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner input=new Scanner(System.in);
int n=input.nextInt();
char[] arr=new char[n];
arr=input.next().toCharArray();
int i,j,k=1,m=0,cishu=0,x,y,z;//cishu用于累积移动次数
//缓存(字符交换时,缓存字符) 中间(回文串中应该放在中间的数)
char huancun,zhongjian='0';
String shuchu="*";
//字符串的字符数是偶数的情况
if(n%2==0){
outer:
for(i=0;i<n;i++){
k=1;
//找出arr中有几个相同的arr[i],并用k记录个数
for(j=0;j<n;j++){
if(i==j) continue;
if(arr[i]==arr[j]){
k++;
}
}
//如果出现奇数个,则不可能形成回文串
//因为字符串字符总数是偶数个
if(k%2==1){
shuchu="Impossible";
break outer;//此时不用再检测其他字符
}
}
//满足形成回文串的条件,用cishu记录最少交换次数
//从外围向中间整理回文串,即根据最左边的字符,从最右边
//开始寻找第一个与其相同的字符,然后移动字符并记录移动次数
if(shuchu=="*"){
cishu=0;
for(i=0;i<(n/2);i++){
outer:
for(j=n-1-i;j>i;j--){
if(arr[i]==arr[j]){
cishu+=n-1-i-j;
//交换字符,且交换n-1-i-j次
for(k=j;k<n-1-i;k++){
huancun=arr[k];
arr[k]=arr[k+1];
arr[k+1]=huancun;
}
break outer;
}
}
}
}
}
//字符串的字符个数是奇数个时
else{
//判断是否可以形成回文串
outer:
for(i=0;i<n;i++){
k=1;
for(j=0;j<n;j++){
if(i==j) continue;
if(arr[i]==arr[j]){
k++;
}
}
if(k%2==1){
//用m记录出现了多少次奇数个的字符,当m>=2时,不能形成回文串
m++;
//if语句不能放在zhongjian的赋值后
//否则第二个开始的奇数个的字符会重新赋值给zhongjian
//那么arr[i]恒等于zhongjian,if语句永远得不到执行
if(m>1&&arr[i]!=zhongjian){
shuchu="Impossible";
break outer;//此时无需再接着判断后续字符
}
zhongjian=arr[i];//记录需要放在中间的数的字符
}
}
//满足交换后可成为回文串的条件
if(shuchu=="*"){
cishu=0;
outer:
for(i=0;i<=(n/2+1);i++){
for(j=n-1-i;j>=i;j--){
//j!=i意味着可以找到除arr[i]本身外与其相同的字符
if(arr[i]==arr[j]&&j!=i){
cishu+=n-1-i-j;
huancun=arr[j];
for(k=j;k<n-1-i;k++){
arr[k]=arr[k+1];
}
arr[n-1-i]=huancun;
break;
}
//j==i意味着arr[i](用v代表)是需要放在最中间的那个字符
//原先我是将v放着不管,先对剩余的字符排好序
//再将计算将v放入最中间需要移动几次
//但是,这并不是最优解决方法
//于是改用:当遇到v时,将v与剩余未排好序的字符看成一个整体
//以最右边的字符为基准,从最左边开始寻找与之相同的字符并移动
//在该方法下,v最后不用移动就处于最中间
if(j==i){
//循环条件不是x>i,而是取到临近字符串最中间那个位置即可
for(x=n-1-i;x>(n-1)/2;x--){
for(y=n-1-x;y<x;y++){
if(arr[y]==arr[x]){
cishu+=(y-(n-1-x));
huancun=arr[y];
for(z=y;z>(n-1-x);z--){
arr[z]=arr[z-1];
}
arr[n-1-x]=huancun;
break;
}
}
}
break outer;
}
}
}
}
}
//输出
if(shuchu=="Impossible") System.out.print(shuchu);
else System.out.print(cishu);
}
}
贪心算法:局部最优以达整体最优
附其中一个评测数据:
(不能只考虑某些类型的字符串,应让代码更为严谨)