完美的代价:贪心算法(递归)

import java.util.*;
import java.util.Date;
import java.math.BigInteger;
/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 简天成
 * Date: 2022-01-11
 * Time: 15:08
 */
public class Main {
    /**
     * //分析:
     * //1.Impossible
     * //奇数个字符出现的次数为1或0时才能实现回文,如果出现奇数的次数>=2,不能实现回文
     * //如madam只出现了一个奇数个字符d可以实现回文,如madfam,出现了两个奇数个字符d和f,不能实现回文
     * /*要判定能否出现回文,就需要统计每个字符出现的次数,这里我们用长度为26的数组来记录,
     * * 因为a-z正好是26个字母。由于题目规定是小写字母,而a的ASCII码值为97,如果当前字符为a
     * * 那么97-97为0,我们就把它放到数组下标为0的位置并将此位置记录为1,如果后面,再出现a,那么
     * * 数组下标为0的位置+1。b、c……z也是如此,这样我们就实现了字符的计数。
     * //如果可行,那么我们就要实现回文交换
     * *  //mamad;可以使用循环交换,也可以使用递归交换,这里我们选用递归交换
     * * //2.查找最后一个字符出现的位置
     * * //3.将一个字符交换到指定的位置
     * * //4.奇数个字符的处理
     * * //如dmama就需要先将d和m交换成mdmama再查找最后一个m出现的位置然后交换。
     */

    private static int N;
    private static char[] arr;
    private static int count;//记录交换次数

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        arr = sc.next().toCharArray();//toCharArray:将字符串转换为字符数组
        sc.close();
        int[] numArr = new int[26];//用于记录每个字符出现的次数
        int flag = 0;//对出现次数为奇数的字符进行计数
        for (int i = 0; i < arr.length; i++) {
            numArr[arr[i] - 'a']++;//这里注意:arr[i]里面放的是输入的字符,那么arr[i]-'a',该字符处于26字母的第几位,
            //arr[i]-'a'也就是字母几,假如是c,c是第三位,arr[i]-'a'=3,说明numArr[3]++,也就是说明c字符出现过一次
            //一共循环26次,记录26字符出现的次数,然后再去用判断次数%2==1,看看是否出现奇数的字符的个数
            //统计只出现奇数次的字母的个数
        }
        //统计出现次数为奇数的字符的个数
        for (int i = 0; i < 26; i++) {
            if (numArr[i] % 2 == 1)
                ++flag;
        }
        //如果出现次数为奇数的字符的个数多于1个,则不能构成回文串
        if (flag > 1)
            System.out.println("Impossible");
        else {//否则,我们去进行查找,但是循环只需要循环一半即可,因为你把前面的一半进行交换完了,后面的都对称了。
            for (int i = 0; i < N / 2; i++) {
                swap(LastIndex(i), N - i - 1);
            }
            System.out.println(count);
        }
    }

    //交换,start:表示要交换的开始的位置;end:表示交换的结束的位置
    private static void swap(int start, int end) {
        if (start == end || arr[start] == arr[end]) {
            return;//此时不需要交换
        }
        char temp = arr[start];
        arr[start] = arr[start + 1];
        arr[start + 1] = temp;
        count++;
        swap(start + 1, end);
    }

    //查找最后一次出现的位置
    private static int LastIndex(int cur) {
		/*mamadm,比如说对于第二个字母a,我们查找的时候就应该从倒数第二个位置开始
		//cur=1,N=6;那么我们就应该从倒数第二个位置即下标为4开始倒着查找,4=N-cur-1;
		从后倒着查找到下标为cur时就应该放弃*/
        for (int i = N - cur - 1; i > cur; i--) {
            if (arr[cur] == arr[i])
                return i;
        }
        //如果从后倒着查找到下标为cur时还未找到相同的字符,那么就说明此字符为奇数个,而且这个奇数字符在第一位
        //为什么要去交换呢???,把它交换到第二位呢?,因为我们在上面进行一半循环开始的时候,从i=0开始,我们直接从第一个i=0开始进行
        //交换,自然你需要保证你的第一位是个成对字符,不能为奇数字符
        //对于出现次数为奇数的字符串,就要先交换,再查找
        swap(cur, cur + 1);
        return LastIndex(cur);//交换过后重新查找一次
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值