package live.every.day.ProgrammingDesign.CodingInterviewGuide.String;

/**
 * 添加最少字符使字符串整体都是回文字符串
 *
 * 【题目】
 * 给定一个字符串str,如果可以在str的任意位置添加字符,请返回在添加字符最少的情况下,让str整体都是回文字符串的一种结果。
 *
 * 【进阶题目】
 * 给定一个字符串str,再给定str的最长回文子序列字符串strlps,请返回在添加字符最少的情况下,让str整体都是回文字符串的一
 * 种结果。进阶问题比原问题多了一个参数,请做到时问复杂度比原问题的实现低。
 *
 * 【难度】
 * 困难
 *
 * 【解答】
 * 进阶问题。
 *
 * 如果有最长回文子序列字符串strlps,那么求解的时间复杂度可以加速到O(N)。如果str的长度为N,strlps的长度为M,则整体回
 * 文串的长度应该是2xN-M。本文提供的解法类似"剥洋葱"的过程,给出示例来具体说明:
 *
 * str="A1BC22DE1F",strlps="1221"。res=...长度为2xN-M...
 * 洋葱的第0层由strlps[O]和strlps[M-1]组成,即”1...1”。从str最左侧开始找字符'1',发现'A'是str第0个字符,'1'是
 * str第1个字符,所以左侧第0层洋葱图外的部分为"A",记为leftPart。从str最右侧开始找字符'1',发现右侧第0层洋葱圈外的部
 * 分为"F",记为rightPart。把(leftPart+rightPart的逆序)复制到res左侧未设值的部分,把(rightPart+leftPart逆序)
 * 复制到res的右侧未设值的部分,即result变为"AF..FA"。把洋葱的第0层复制进res的左石两侧未设值的部分,即result变为
 * "AF1..1FA"。至此,洋葱第0层被剥掉。洋葱的第1层由strlps[1]和strlps[M-2]组成,即”2...2”。从str左侧的洋葱第0层往
 * 右找"2",发现左侧第1层洋葱圈外的部分为"BC",记为leftPart。从str右侧的洋葱第0层往左找"2",发现右侧第1层洋葱圈外的
 * 部分为"DE",记为rightPart。把(leftPart+rightPart的逆序)复制到res左侧未设值的部分,把(rightPart+lefPart逆序)
 * 复制到res的右侧未设值的部分,res变为"AF1BCED..DECB1FA"。把洋葱的第1层复制进res的左右两侧未设值的部分,即result
 * 变为"AF1BCED2..2DECB1FA"。第1层被剥掉,洋葱剥完了,返回"AF1BCED22DECB1FA"。
 *
 * 整个过程就是不断找到洋葱圈的左部分和右部分,把(lefPart+rightPart的逆序)复制到res左侧未设值的部分,把
 * (rightPart+leftPart逆序)复制到res的右侧未设值的部分,洋葱剥完则过程结束。具体请参看如下的getPalindrome2方法。
 *
 * @author Created by LiveEveryDay
 */

public class AddMinCharsMakePalindrome2 {

    public static String getPalindrome2(String str, String strlps) {
        if (str == null || str.equals("")) {
            return "";
        }
        char[] chas = str.toCharArray();
        char[] lps = strlps.toCharArray();
        char[] res = new char[2 * chas.length - lps.length];
        int chasLeft = 0;
        int chasRight = chas.length - 1;
        int lpsLeft = 0;
        int lpsRight = lps.length - 1;
        int resLeft = 0;
        int resRight = res.length - 1;
        int tmpLeft = 0;
        int tmpRight = 0;
        while (lpsLeft <= lpsRight) {
            tmpLeft = chasLeft;
            tmpRight = chasRight;
            while (chas[chasLeft] != lps[lpsLeft]) {
                chasLeft++;
            }
            while (chas[chasRight] != lps[lpsRight]) {
                chasRight--;
            }
            set(res, resLeft, resRight, chas, tmpLeft, chasLeft, chasRight, tmpRight);
            resLeft += chasLeft - tmpLeft + tmpRight - chasRight;
            resRight -= chasLeft - tmpLeft + tmpRight - chasRight;
            res[resLeft++] = chas[chasLeft++];
            res[resRight--] = chas[chasRight--];
            lpsLeft++;
            lpsRight--;
        }
        return String.valueOf(res);
    }

    private static void set(char[] res, int resl, int resr, char[] chas, int ls, int le, int rs, int re) {
        for (int i = ls; i < le; i++) {
            res[resl++] = chas[i];
            res[resr--] = chas[i];
        }
        for (int i = re; i > rs; i--) {
            res[resl++] = chas[i];
            res[resr--] = chas[i];
        }
    }

    public static void main(String[] args) {
        String str = "A1B21C";
        String strlps = "121";
        System.out.printf("The palindrome is: %s", getPalindrome2(str, strlps));
    }

}

// ------ Output ------
/*
The palindrome is: AC1B2B1CA
*/