分治法POJ 1854

与世无争的帅哥 提交于 2020-02-03 01:55:40

分治法:POJ1854 Evil Straw Warts Live

问题:
Description
A palindrome is a string of symbols that is equal to itself when reversed. Given an input string, not necessarily a palindrome, compute the number of swaps necessary to transform the string into a palindrome. By swap we mean reversing the order of two adjacent symbols. For example, the string “mamad” may be transformed into the palindrome “madam” with 3 swaps:
swap “ad” to yield “mamda”
swap “md” to yield “madma”
swap “ma” to yield “madam”
Input
The first line of input gives n, the number of test cases. For each test case, one line of input follows, containing a string of up to 8000 lowercase letters.
Output
Output consists of one line per test case. This line will contain the number of swaps, or “Impossible” if it is not possible to transform the input to a palindrome.
Sample Input
3
mamad
asflkj
aabb
Sample Output
3
Impossible
2

题目大意就是给你一个字符串,判断相邻元素交换几次他能变成回文序列,如果无论怎么变化他都不能出现回文的话就输出个Impossible 否则输出交换的次数 例子给的很清楚了,不再过多赘述。

首先考虑判断能否回文的方法:1.奇数个的字符不止一个的情况下就不可能回文
例如:abbccc a与c出现的次数分别为 1、3 则不会出现回文

这题我采用了分治的思想,先把最外侧的匹配一样了,再逐步往内,缩小字符串,子字符串又是与原字符串一样的问题,只是规模更小。
1.merge(char [] array , int head , int tail)方法 array为数组,head为首个字符,tail为末尾字符
定义left = array[head] 然后使用一个指针i 从 tail 开始向内遍历,直到left==array[i]
记录下该指针的位置,用tail-i 就为需要交换的次数 定义为left_count
再定义 right = array[tail] 使用指针 I 从 head开始向内遍历,直到right == array[i]
记录下指针位置,用i-head就为需要交换次数 定义为 right_count
(如果left_count == right_count == 0 )的话则表明左右两边都没有找到一样的,则有两个出现奇数的字符 输出Impossible
2比较左右两边需要交换的次数,取小的一侧,开始交换位置。使用swap函数来完成
3.递归调用merge(array , head+1 , tail-1 ) 就可以再去解决该问题的子问题

例如:
在这里插入图片描述
AC代码:(Java)

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        //主函数 接收数据
        Scanner scanner = new Scanner(System.in);
        int nums = scanner.nextInt();
        while (nums > 0) {
            count = 0 ;
            String str = scanner.next();
            char [] array = str.toCharArray() ;
            merge(array,0,array.length-1);
            if (flag == true) {
                System.out.println(count);
            };
            nums-- ;
        }

    }
    static int count ;  //用于记录交换次数
    //用于区别Impossible 若flag == true则输出交换次数,如果是false就不输出交换次数,只输出Impossioble
    static boolean flag  ;
    static void merge(char [] array , int head , int tail){
        flag = true ;   //每次调用方法先初始化一下
        if (head>=tail){    //递归出口
            return;
        }
        char left = array[head];    //最左边值
        char right = array[tail];   //最右边值
        int left_location = 0 ;        //从右开始 当找到与最左边的值相同时的边位置 如mamad 从右开始第三个找到 那么他的值就是该位置索引就是2
        int right_location = 0 ;        //从左开始 当找到与最右边的值相同时的右边位置 如mamad 没找到与 d 相同的那么他就是0
        for (int i = tail; i > head; i--) { //从右边开始遍历找
            if (left == array[i]) {
                left_location = i;
                break;
            }
        }
        for (int i = head; i < tail; i++) { //从左边开始遍历找
            if (right == array[i]) {
                right_location = i;
                break;
            }
        }
        if (left_location ==0 && right_location == 0) {   //判断如果 右边左边都没找到 那么两个值就都是 0 则为Impossible
            System.out.println("Impossible");
            flag = false ;                  //让flag==false 到时候就只输出Impossible
            return;
        }

        if (left_location == 0 && right_location != 0) {  //右边找到 左边没找到 那就是右边交换
            count+=right_location-head;
            for (int i = right_location; i > 0 ; i--) {    //从索引处开始 一直与下一个字符交换直到他放到最外面
                swap(array,i,i-1);
            }
        }
        else if (left_location != 0 && right_location == 0) {    //左边找到 右边没找到 那就是左边交换
            count+=tail-left_location ;
            for (int i = left_location; i < tail; i++) {   //从索引处开始 一直与上一个字符交换直到他放到最外面
                swap(array,i,i+1);
            }
        }else if (left_location != 0 && right_location != 0 &&(right_location-head)-(tail-left_location)>=0){
            //两边都找到 但是右边开始找 交换次数更小, 那么采用右边的
            count+=tail-left_location ;
            for (int i = left_location; i < tail; i++) {
                swap(array,i,i+1);
            }
        }else if (left_location != 0 && right_location != 0 &&(right_location-head)-(tail-left_location)<=0){
            //两边都找到 但左边开始找 交换次数更小, 那么采用右边的
            count+=right_location-head;
            for (int i = right_location; i > 0 ; i--) {
                swap(array,i,i-1);
            }
        }
        merge(array,head+1,tail-1); //递归调用 把问题缩小,子问题与原问题相比只是规模更小
    }

    /**
     * 交换位置
     * @param array
     * @param i
     * @param j
     */
   static void swap(char [] array , int i, int j) {
        char temp = array[i];
        array[i] = array[j];
        array[j] = temp ;
    }
}

这个算法如果在最差的情况下就是每一次左右两边都搜索了最大次数,就是个等差数列2*[(1+n) * n /2] 所以是*O(n²)级别的复杂度
PS:如果有错的地方,记得告诉我!!!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!