manacher算法 (O(n)求最长回文子串)
2014-06-06 00:16
323 查看
1. 前言
我们可以用O(n^2)暴力求解最长回文子串。
之所以是这个复杂度,是因为我们对每个字符比较其两边元素是否相等时,我们都是从它最旁边的一个开始迭代的。
但如果我们能以该字符为中心,其附近的某一段子串已为回文,在此基础上比较更远的元素,那么就有可能降低这个复杂度了。
2. 定义及预处理
将字串str扩展成string s(2*str.size()+1), 在str的两端及每个字符间加上个特殊符号,在此使用#
如str = "axamppm", 则s = "#a#x#a#m#p#p#m#".
这样的好处是可以同时处理回文长度为奇数或偶数的情况:因为奇数回文的中心是一个字符,偶数的则为两个字符。在扩展后得s中,最长回文子串的中心一定为一个字符(特殊字符# 或 原本str中的字符)
定义辅助数组vector<int> p(2*str.size()+1), p表示:字串s中对应下标的字符 在s中向右延伸仍为回文的最长长度。如
s: # a # x # a # m # p # p # m #
p: 1 2 1 3 1 2 1 2 1 2 4 2 1 2 1
mx为在当前迭代i之前,x+p[x]的最大值(即最远长度)
id为取得mx的x值。
3. 性质
如图1所示,mx > i, 且mx > i + p[j] (这个约束在图2中解释)
由于id一定在i左边,且以j为中心的回文串完全处在一个回文串的左半部分。那么其对称点i有性质 p[i] = p[j].
我们即可以结束这次迭代,考察下一个点i+1.
图2为 mx > i, 且mx < i + p[j]的情况
此时因为以 j 为中心的最长回文串只是部分处在一个回文串的左半部分。那么其对称点i有性质 p[i] = mx - i;
此时因继续比较s[ i-p[i]-1 ]及s[ i+p[i]+1 ]的大小。
4. 代码实现
5. 复杂度证明
考察每次迭代,
当mx > i时,
图1的情况,p[i] = p[j]后迭代结束,O(1)
图2的情况,初始化p[i] = mx - i, 之后每次运算都将移进mx.
当mx < i时,
若中心点i左右匹配不成功,迭代结束,O(1)
若匹配成功,每次运算都将移近mx.
所以只有O(N)次匹配失败,此外每次成功匹配,都将移近mx. mx共移进O(N)
由此复杂度为O(N)
我们可以用O(n^2)暴力求解最长回文子串。
之所以是这个复杂度,是因为我们对每个字符比较其两边元素是否相等时,我们都是从它最旁边的一个开始迭代的。
但如果我们能以该字符为中心,其附近的某一段子串已为回文,在此基础上比较更远的元素,那么就有可能降低这个复杂度了。
2. 定义及预处理
将字串str扩展成string s(2*str.size()+1), 在str的两端及每个字符间加上个特殊符号,在此使用#
如str = "axamppm", 则s = "#a#x#a#m#p#p#m#".
这样的好处是可以同时处理回文长度为奇数或偶数的情况:因为奇数回文的中心是一个字符,偶数的则为两个字符。在扩展后得s中,最长回文子串的中心一定为一个字符(特殊字符# 或 原本str中的字符)
定义辅助数组vector<int> p(2*str.size()+1), p表示:字串s中对应下标的字符 在s中向右延伸仍为回文的最长长度。如
s: # a # x # a # m # p # p # m #
p: 1 2 1 3 1 2 1 2 1 2 4 2 1 2 1
mx为在当前迭代i之前,x+p[x]的最大值(即最远长度)
id为取得mx的x值。
3. 性质
如图1所示,mx > i, 且mx > i + p[j] (这个约束在图2中解释)
由于id一定在i左边,且以j为中心的回文串完全处在一个回文串的左半部分。那么其对称点i有性质 p[i] = p[j].
我们即可以结束这次迭代,考察下一个点i+1.
图2为 mx > i, 且mx < i + p[j]的情况
此时因为以 j 为中心的最长回文串只是部分处在一个回文串的左半部分。那么其对称点i有性质 p[i] = mx - i;
此时因继续比较s[ i-p[i]-1 ]及s[ i+p[i]+1 ]的大小。
for ( ; i-p[i]-1>=0 && i+p[i]+1<str.size() && str[i-p[i]-1] == str[i+p[i]+1]; ++ p[i]) {}在迭代结束后,更新mx及id.
4. 代码实现
class Solution { public: string longestPalindrome(string s) { string str(2*s.size()+1, 0); // str for (size_t i = 0, j = 0; i < str.size(); ++ i) { if (i % 2 == 0) { str[i] = 0; } else { str[i] = s[j ++]; } } // p int mx = 0, id, max_length=0, max_id; vector<int> p(2*s.size()+1, 0); for (int i = 0; i < str.size(); ++ i) { p[i] = mx>i? min(p[2*id-i], mx-i): 0; for ( ; i-p[i]-1>=0 && i+p[i]+1<str.size() && str[i-p[i]-1] == str[i+p[i]+1]; ++ p[i]) {} if (i + p[i] > mx) { mx = i + p[i]; id = i; } if (max_length < p[i]) { max_length = p[i]; max_id = i; } } return s.substr((max_id-p[max_id])/2, p[max_id]); } };
5. 复杂度证明
考察每次迭代,
当mx > i时,
图1的情况,p[i] = p[j]后迭代结束,O(1)
图2的情况,初始化p[i] = mx - i, 之后每次运算都将移进mx.
当mx < i时,
若中心点i左右匹配不成功,迭代结束,O(1)
若匹配成功,每次运算都将移近mx.
所以只有O(N)次匹配失败,此外每次成功匹配,都将移近mx. mx共移进O(N)
由此复杂度为O(N)
相关文章推荐
- 最长回文子串(Manacher算法)
- 最长回文子串的manacher算法
- KT学算法(三)——最长回文子串与Manacher算法
- 最长回文子串,Manacher算法
- 求最长回文子串的Manacher算法
- HiHo 1032 最长回文子串 (Manacher算法求解)
- 练习:最长回文子串(Manacher算法)
- Poj3974 最长回文子串 Manacher算法
- 51nod_1089 最长回文子串 V2(Manacher算法)
- Hihocoder #1032 : 最长回文子串 (Manacher算法)
- Manacher算法: O(n)时间求字符串的最长回文子串
- 最长回文子串(Manacher算法)
- 最长回文子串:manacher算法
- 【转】最长回文子串的O(n)的Manacher算法
- 最长回文子串(Manacher算法)
- [51Nod](1089)最长回文子串 V2 ---- Manacher算法
- 最长回文子串 manacher算法
- 最长回文子串--轻松理解Manacher算法
- LeetCode5. Longest Palindromic Substring(最长回文子串:Manacher算法)
- Manacher算法——找字符串最长的回文子串