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); }
自此马拉车算法算是结束了
我虽然模糊解释了很多概念 首先声明这个算法确实复杂我只是提供一个理解思路
很多人不是看不懂 而是想一次理解这个算法所以很难受 这也是我看懂这个解法的一点收获
先分解然后逐步破译 希望有缘人少一点弯路

浙公网安备 33010602011771号