Manacher
问题
寻找字符串中的最长回文串
传统做法
-
字符串首字符前加一个特殊字符
‘#’
末尾字符加一个特殊字符‘#’
相邻字符间也加上特殊字符‘#’
-
遍历字符串,除特殊字符外,以每个字符作为回文字符串的中心向外扩张
思考
很明显这种做法的时间复杂度是很高的,所以采用manacher
进行提速
提速
数据
记录数据R
表示目前最长回文串的的半径
记录数据M
表示目前最长回文串的中心
数组parr
存储每个字符为中心的最长回文串的半径
思路
根据遍历字符下标i
和R
的关系分为以下情况
-
i
在R
范围外,则自己往外扩 -
i
在R
范围内,根据以M
做对称点的i'
情况分为三种情况
-
parr[i']
在R
范围内,但不与R
边缘位置重叠,则parr[i] = parr[i']
-
parr[i']
超出R范围,则parr[i] = R - i
-
parr[i']
在R
范围内,与R
边缘位置重叠,也是自行扩,有一段距离不用判断:R - i
实现
string f1(string str) { string res; for(int i = 0;i < str.size() * 2 + 1;i++) { res += i & 1 ? str[i / 2] : '#'; } return res; } int f2(string str) { if(str.size() == 0) return 0; int R = -1; int M = -1; string pstr = f1(str); int* parr = new int(pstr.size()); int Rmax = INT32_MIN; for(int i = 0;i < pstr.size();i++) { /******不用判断的半径******/ int parr[i] = R > i ? min(parr[2 * M - i],R - i) : 1; while(i + parr[i] < pstr.size() && i - parr[i] > -1) { if(str[i + parr[i]] == str[i - parr[i]]) { parr[i]++; } else break; } if(parr[i] + i > R) { R = i + parr[i]; M = i; } Rmax = max(Rmax,parr[i]); } delede[] parr; return Rmax - 1; }
思考
parr[i'] 在R范围内
R
范围内已是回文串且以M
为中心点,所以M
做对称点的i'
的半径结果,就是i
的半径结果
parr[i'] 超出R范围内
i的半径结果为R - i
,不可能为更大
R - i 为什么不可能更大
如图,假设i开始还可以往外扩,则? = b
;以M为中心,?
的对称点也为b
,那R
的值应该更大才对,可是R
之前并没有扩到那么大,所以假设不成立
parr[i'] 在R范围内,但与R重叠
此情况下i
是可以往外扩的,不会影响R值,可以用上面的方法印证
总结
不同情况下,扩与不扩分析,需要理解清楚