LeetCode 最长字符串 马拉车解法-

马拉车算法没那么复杂 只是想一次性读懂确实比较难  我们换个思路然后容易理解了

第一 进行字符串预处理 字符串经过预处理后就可以转为奇数串  至于那个$  与 ^  这在某大佬的博客里称为哨兵位看的太多记不住谁说的了 见谅

  public static String preString(String s) {
        String s1 = "$#";
        for (int i = 0; i < s.length(); i++) {
            s1 += s.charAt(i) + "#";
        }
        s1+="^";

        return s1;
    }

然后我们进行 中心扩展法进行计算

 public static  String lo1(String s){
        String s1 = preString(s);
        int length = s1.length();
        int [] P = new int[length];
        

        for (int i = 1; i < length-1 ; i++) {
            
            while(s1.charAt(i-1-P[i])==s1.charAt(i+1+P[i])){
                P[i]++;
            }
        }
        
        int maxLen = 0;
        int centerIndex = 0;
        for (int i = 1; i < length - 1; i++) {
            if (P[i] > maxLen) {
                maxLen = P[i];
                centerIndex = i;
            }
        }
        int start = (centerIndex - maxLen) / 2;
        return s.substring(start, start + maxLen);
    }

自此中心扩展解法就完成了 我感觉这道题被定为 中等难度可能也因为如此

马拉车算法要维护两个变量  一个是中心点 C 一个是最右臂长R

因为基于中心C对称  所以   中心点右侧的对称关系与 左边一样  我们理解为镜像赋值

镜像赋值有三种关系 

第一  如果 计算最终结果不超过  R  又不涉及到边界问题 便直接赋值

第二  超过右边界  此时 在右边界以内的可以被利用 

第三 左边界临界  此时  左边界因为没有值所以无法准确 反映其对应点的对称关系 

重点来了  第二点和第三点在什么条件下成立    是不是在不超过最有边界的情况下成立 

所以我们就可以理解 马拉车算法的核心内容  

R-i可以保证不超R  所以R-i 与 镜像值 P[i_mirror] 之间选最小值永远不会超出最右边界的R的范围

if (R > i) {
    P[i] = Math.min(R - i, P[i_mirror]);
}else {
    P[i] = 0;
}

自此我们还剩下两个问题

第一镜像位如何求      

  int i_mirror = 2 * C - i;

这么看不大容易理解 
我们换种方式
 int i_mirror = C - (i-C);

仔细看看C是中心点的位置  i 是中心点右侧的偏移量

这样是不是就理解了

最后一个问题

什么时候变更C (中心点) 以及R右边界

  if (i + P[i] > R) {
                C = i;
                R = i + ints[i];
            }

变更时候当然是 最优边界发生变化的时候

这个数组P就是臂长   

然后我们将他们组合起来


public static String preprocess(String str) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("$");
for (int i = 0; i < str.length(); i++) {
stringBuilder.append("#");
stringBuilder.append(str.charAt(i));
}
stringBuilder.append("#^");
return stringBuilder.toString();
}


public static String lo(String temp) { String p = preprocess(temp); int length = p.length(); int[] P = new int[length]; int C = 0; int R = 0; for (int i = 1; i < P.length - 1; i++) { int i_mirror = 2 * C - i; if (R > i) { P[i] = Math.min(R - i, P[i_mirror]); } else { P[i] = 0; } while (p.charAt(i + 1 + P[i]) == p.charAt(i - 1 - P[i])) { P[i]++; } if (i + P[i] > R) { C = i; R = i + P[i]; } } int maxLen = 0; int centerIndex = 0; for (int i = 1; i < length - 1; i++) { if (P[i] > maxLen) { maxLen = P[i]; centerIndex = i; } } int start = (centerIndex - maxLen) / 2; return temp.substring(start, start + maxLen); }

自此马拉车算法算是结束了

我虽然模糊解释了很多概念  首先声明这个算法确实复杂我只是提供一个理解思路

很多人不是看不懂 而是想一次理解这个算法所以很难受 这也是我看懂这个解法的一点收获

先分解然后逐步破译  希望有缘人少一点弯路

 

posted @ 2021-09-22 16:41  OTeam  阅读(90)  评论(0)    收藏  举报