LeetCode解题报告程序员

LeetCode 5

2017-03-24  本文已影响60人  77即是正义

题目

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

样例

思想

这里来说一下O(n)来解决最长回文串的问题,即Manacher算法

那么首先需要说一个O(n2)的方法,然后才能体会O(n)的方法的真正巧妙的地方在哪里。O(n2)的方法是枚举一个中心点i,然后向左右两边查找,直到发现Str[i-k]与Str[i+k]不相等了,或者是越界了,再开始下一次中心点的枚举。然后对于"baab"这样没有中心点的对称回文,又要用相同的方法再做一次(中心点变成两个)。

大家就会发现如果遇到了像"cccccc",这样多次重复的串,那么一个位置就会被重复访问多次,这就是它很慢的根本原因,并且奇偶分别解决的话也非常的麻烦。那么我们就需要想办法来规避掉这两个问题。对于搜索状态的重复性的解决方案一般就是利用DP(对于无后效性的问题)。那么我们就使用DP的思想再来思考一下这个问题,就会发现有一些状态是可以被继承的。因为这是回文串,那么就会出现a与b关于c成镜像的情况,进一步在以c为中心的回文串当中,以a为中心的回文串和以b为中心的回文串一定是相同的。(这里没看懂不要紧,后面会具体画图分析)

那么我们就利用这个特性,就能解决掉重复访问的问题。对于奇偶的问题,我们就可以使用插入'#'号的方法来规避掉。(这个办法真是太巧妙了)

baab => #b#a#a#b#
bacab => #b#a#c#a#b#

这样就把奇偶的回文串全部转化成了奇数的回文串。

好了,那下面进入正题,怎么解决掉重复访问的问题?

例如:bacab => #b#a#c#a#b#
原字符串的长度为5,变形后的长度为11。
RL[6] = 6,即在变形串中以c为中心的最长回文串的半径为6。
那么整个回文串的长度应为L = 2 * RL[6] -1。
因为该回文串首尾必定为'#',所以随便去掉首部或者尾部的'#'后,该回文串为原回文串长度的2倍。
所以原回文串长度L` = [2 * RL[6] - 2] / 2 = RL[6] - 1

以上图片均来自网上博客

代码

  public int extendIndex(char[] charArr, int i, int l, int r) {
    while (l - 1 >= 0 && r + 1 < charArr.length) {
      if (charArr[l - 1] == charArr[r + 1]) {
        l --;
        r ++;
      } else {
        break;
      }
    }
    return (i - l + 1);
  }

  public String longestPalindrome(String s) {
    char[] _s = s.toCharArray();
    char[] charArr = new char[_s.length * 2 + 1];
    int[] f = new int[charArr.length];
    int id = -1, maxRight = -1, ansId = 0, max = -1;
    charArr[0] = '#';
    for (int i = 1; i <= _s.length; i++) {
      charArr[i * 2 - 1] = _s[(i - 1)];
      charArr[i * 2] = '#';
    }

    for (int i = 0; i < charArr.length; i++) {
      if (i > maxRight) {
        f[i] = extendIndex(charArr, i, i, i);
      } else {
        // i 关于id的镜像
        int j = 2 * id - i;
        if (i + f[j] - 1 >= maxRight) {
          f[i] = extendIndex(charArr, i, 2 * i - maxRight, maxRight);
        } else {
          f[i] = f[j];
        }
      }
      if (i + f[i] - 1 > maxRight) {
        maxRight = i + f[i] - 1;
        id = i;
      }
      if (f[i] > max) {
        max = f[i];
        ansId = i;
      }
    }

    char[] res = new char[(max - 1)];
    int index = 0;
    for (int i = -max + 1; i < max; i++) {
      if (charArr[ansId + i] != '#') {
        res[index] = charArr[ansId + i];
        index ++;
      }
    }
    String resStr = String.valueOf(res);
    return resStr;
  }

代码不够精简,后续会更新一个精简版

复杂度分析

以上进行了算法的分析,那最后分析一下这种算法的复杂度和在O(n^2)算法的基础上的提高。

上一篇 下一篇

猜你喜欢

热点阅读