马拉车(Manacher)算法 - 解最小回文子串

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28666081/article/details/86494982
什么是回文字符串?
顺着反着输出都一样的就是回文字符串。如“abccba”、“level”
 
如何解最小回文子串:
1 在当前字符串的每个字符左右侧插入"#",并在该字符串前后加入$...@用于标记边界,例子为"abc"=>"$#a#b#c#@"
2 初始化一个p[i]数组。用于存储当前下标i对应的半径长度,如"$#a#b#c#@"对应的p数组为[1, 1, 2, 1, 2, 1, 2, 1, 1],由上面例子可见如果回文子串是本身的,那p[i] = 1;
3 循环处理加'#'后的字符串:
    3.1 取值p[i],其计算公式为(重点!!):p[i] = mx > i ? Math.min(p[2 * id - i], mx - i) : 1;
    其各参数含义分别如下:
    - mx: 已找到回文的最右端点
    - i: 当前点
    - id: 已找到回文的中点
    - (2 * id - i): i点关于id对称的点,(2 * id - i) <= id <= i
    - (mx - i): i到max的回文半径
4 while增加当前p[i]到最大(因为上面只能判断到mx-i的距离)
5 判断当前mx与当前i,并如果当前i大于上一个回文字符串长度的话,则重新将当前i的替换掉 mx、id
6 如果当前长度大于最大,则记录下来
7 往上重复3~6过程
 
代码如下:
public class Demo12 {
    private static String manacher(String s) {
        // 1 给字符串中的每个字符均插入'#'转为奇数串
        StringBuilder t1 = new StringBuilder("$#"); //$ 用于标记字符串开头
        for (int i = 0; i < s.length(); ++i) {
            t1.append(s.charAt(i));
            t1.append("#");
        }
        t1.append("@");
        String t = t1.toString(); //@ 用于标记字符串结尾
        // 2 处理加了'#'后的字符串,并找出结果
        int[] p = new int[t.length()]; //初始化p[i]:i对应的半径长度
        // 初始化标识=>>mx-回文字符串最右端、id-回文串中点、resLen-结果的长度、resCenter-结果的中点
        int mx = 0, id = 0, resLen = 0, resCenter = 0;
        for (int i = 1; i < t.length() - 1; ++i) {
            // 获取当前i对应的半径长度
            // 为什么要下面这样比较?
            // - 1) 判断mx > i,即回文的最后端位置是否大于当前i
            // - 1.1) 小于说明i不包含在当前回文中,即回文为本身,半径=1
            // - 1.2) 大于或等于说明i在回文为mx的最右端,即回文的半径<=p[id],又因为(2 * id - i) <= id <= i <= mx,(2*id-i)为i关于id的左侧对称点
            // - (mx-i)是指i阶段在当前回文范围内的最大值,取(p[2 * id - i], mx - i)较小值
            p[i] = mx > i ? Math.min(p[2 * id - i], mx - i) : 1;
            // 判断i与他的半径加减后是否超出边界,如果不超出边界就往,左右两侧对比,然后增加当前i半径
            while (((i - p[i]) >= 0) && ((i + p[i]) < t.length() - 1) && (t.charAt(i + p[i]) == t.charAt(i - p[i]))) ++p[i];
            // 判断当前i加上他的半径长度超过回文的最右端的话就将当前(i+p[i])的赋给mx,并记录回文id=i `1
            if (mx < i + p[i]) {
                mx = i + p[i]; // 将当前回文的最右端mx置为(i+p[i])
                id = i; // i作为回文的中点
            }
            // 判断最长的结果长度,并做相应存储
            if (resLen < p[i]) {
                resLen = p[i]; //存储结果长度
                resCenter = i; //存储结果的点
            }
        }
        return s.substring((resCenter - resLen) / 2, (resCenter - resLen) / 2 + resLen - 1);
    }
    @Test
    public void test01() {
        System.out.println(Demo12.manacher("waabwswfdwaabwswfd"));
    }
}
 

猜你喜欢

转载自blog.csdn.net/qq_28666081/article/details/86494982