22.1.23Manacher算法、双端队列、单调栈
1.Manacher算法
1)用途:
-
Manacher算法用于解决类似求某个字符串中最长的回文子串。(回文就是正着读和倒着读一样的结构)。
2)算法中的关键变量:
-
回文半径 r
-
回文直径 d:整个回文的长度;
-
之前扩大的所有位置中所到达的回文直径d的最右边界R;
-
中心点c:取得R的那个点;
-
回文半径数组:储存遍历字符串所得到的每个点的回文半径;
3)算法的流程:
-
伪代码:
-
-
这里的R与上述提到的概念不太相同,这里是回文最右边界的下一位。对字符串依次遍历,我们回遇到三种情况:
-
当前遍历到的元素不在回文的有边界R内:此时没什么优化的方法,直接暴力扩大,判断扩大的是否为回文结构。
-
当前遍历到的元素在右边界内:此时可以做出如下的位置关系:(L是R关于中心c的对称点,i'是i(当前遍历的点)的对称点),此时i的回文直径,回文半径都与他的对称点i'相同(根据对称的性质即可证明)。
-
当前遍历到的元素的对称点的回文直径的左边界在L的左边:如下图,此时i的回文半径就是i~R所表示的区域。下面给出证明:x,y分别为L,L'的前一个数和后一个数,k,z也同理。由c扩得的回文区域是L~R所表示的区域,那我们就知道x!=z的,根据i'的会问区域的左边界是超过了L的,我们能知道:至少是x,y也在i'的回文区域内,所以x=y的,y=k,因为x!=z的,所以k!=z。
-
当前遍历到的元素的对称点的回文直径的左边界刚好与L重合:如下图:此时i的回文半径就是i'的回文半径。对称可证。
-
-
code:
public static char[] manacherString(String str) {
char[] charArr = str.toCharArray();
char[] res = new char[str.length() * 2 + 1];
int index = 0;
for (int i = 0; i != res.length; i++) {
res[i] = (i & 1) == 0 ? '#' : charArr[index++];
}
return res;
}
public static int maxLcpsLength(String str) {
if (str == null || str.length() == 0) {
return 0;
}
char[] charArr = manacherString(str);
int[] pArr = new int[charArr.length];
int C = -1;
int R = -1;
int max = Integer.MIN_VALUE;
for (int i = 0; i != charArr.length; i++) {
pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
//
while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {
if (charArr[i + pArr[i]] == charArr[i - pArr[i]])
pArr[i]++;