java 基础练习 完美的代价 蓝桥杯1061
基础练习 完美的代价
Description
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
Input
输入描述:
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输入样例1:
5
mamad
Output
输出描述:
如果可能,输出最少的交换次数。
否则输出Impossible
输出样例1:
3
输入样例2:
abbcac
输出样例2:
3
思路
这道题的思路就是从左到右遍历字符,然后寻找与之相等的字符,将与之相等的字符和它的对称位置上的字符交换即可。但是当我写完我发现题目要求交换一次只能相邻的交换,所以我们上面描述的每一次把与之相等的字符和对称位置上的交换就可以划分成多次的相邻交换,这样就能解决问题。不过需要注意的地方就是交换过程中如果遇到相同字符,则需要跳过此次交换,不然次数就多了。还有一点需要注意就是遍历字符交换之前要先判断该字符串是否可以构成回文,不能则打印Impossible,可以再分偶数字符的字符串和奇数字符的字符串进行遍历。而判断是否可以构成回文只需要计算字符串其中的孤儿字符,即那些没有可以配对的字符的个数,如果是偶数字符的字符串,则不能有孤儿字符,如果是奇数字符的字符串,则只能有一个孤儿字符。
代码
import java.util.Scanner;
public class Main {
static int count = 0;//交换次数
static int half;//计算字符串最中间字符的索引(偶数个字符则取中心左边的字符的索引)
static int countsingle = 0;//单独字符的个数(没有被配对的字符)
static int singleindex = 0;//单独字符的索引
static String[] str;//存放字符串
static int N;//字符个数
static boolean[] used;//检测是否可以构成回文时使用
public static void main(String[] args) {
init();
getResult();
}
private static void getResult() {
//计算单独字符的个数(比如abbaa或者mamad都可计算出单独字符个数为1)
countSingle();
//当字符数为偶数个数
if(N%2==0) {
half = N/2-1;
//偶数字符个数字符串不能有单独字符
if(countsingle>=1) {
System.out.println("Impossible");
return;
}
//从左到右遍历每一个字符,寻找与之相同的字符串,并与对称位置的字符交换
for (int i = 0; i <=half; i++) {
for (int j = i+1; j < str.length; j++) {
if(str[i].equals(str[j])&&!str[i].equals(str[str.length-1-i])) {
//交换两个指定位置的字符
//不过需要注意一次交换只能相邻两个字符
swap(j,str.length-1-i);
break;
}
}
}
}else {//字符个数为单数的字符串
half = N/2;
//单独字符只能为1个
if(countsingle>1) {
System.out.println("Impossible");
return;
}
//将单独字符置于最中间(此时也会计算交换次数)
if(half!=singleindex) {
swap(singleindex,half);
}
//从左到右遍历每一个字符,寻找与之相同的字符串,并与对称位置的字符交换
for (int i = 0; i <half; i++) {
for (int j = i+1; j < str.length; j++) {
if(str[i].equals(str[j])&&!str[i].equals(str[half+(half-i)])) {
swap(j,half+(half-i));
break;
}
}
}
}
//打印结果
System.out.println(count);
}
private static void swap(int x, int y) {
//Math.min 和Math.max是在判断索引的大小
//从左到右开始交换
for (int i = Math.min(x, y); i < Math.max(x, y); i++) {
if(!str[i].equals(str[i+1])) {//这里的判断挺细节,因为交换过程可能会遇到相同的字符,就不用交换
String temp = "";
temp = str[i];
str[i] = str[i+1];
str[i+1] = temp;
count++;
}
}
//再从右到左交换
for (int i = Math.max(x, y)-1; i > Math.min(x, y); i--) {
if(!str[i].equals(str[i-1])) {
String temp = "";
temp = str[i];
str[i] = str[i-1];
str[i-1] = temp;
count++;
}
}
}
private static void init() {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
str = sc.next().split("");
used = new boolean[N];
}
public static void countSingle() {
for (int i = 0; i < str.length; i++) {
//如果该字符没有被配对过则可以尝试配对
if(used[i]==false) {
boolean flag = false;
used[i] = true;
for (int j = 0; j < str.length; j++) {
if(str[i].equals(str[j])&&i!=j&&used[j]==false) {
flag = true;
used[j] = true;
break;
}
}
if(flag == false) {//如果发现单独字符,则保存索引和增加单独字符个数
singleindex=i;
countsingle++;
}
}
}
}
}