回文序列检测,Manacher算法详解
2015-03-27 17:20
218 查看
算法简介:算法的目的是在O(n)的时间复杂度内找到一个字符串中各个字母所在的最大长度的回文串。
此算法用到了一个Rad[]数组的定义,Rad[i]表示回文的半径,即最大的j满足s[i-rad[i],i-1]=s[i+1,i+rad[i]]。
很明显,找到了所有的rad[i],就求出了所有的长度为奇数的回文子串,至于偶数的怎么求,最后再讲.
假设现在求出了rad[1..i-1],现在要求后面的rad值,并且通过前面的操作,得知了当前字符i的rad值至少为j.
现在通过试图扩大j来扫描,求出了rad[i].再假设现在有个指针k,从1循环到rad[i],试图通过某些手段来
求出[i+1,i+rad[i]]的rad值.
根据定义,黑色的部分是一个回文子串,两段红色的区间全等,因为之前已经求出了rad[i-k],所以直接用它.
有3种情况:
1. rad[i]-k<rad[i-k]
如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分超过了黑色的部分,所以rad[i+k]肯定至少为rad[i]-k,
即橙色的部分.那橙色以外的部分就不是了吗?这是肯定的.因为如果橙色以外的部分也是回文的,那么根据青色和红色部分的
关系,可以证明黑色部分再往外延伸一点也是一个回文子串,这肯定不可能,因此rad[i+k]=rad[i]-k.为了方便下文,
这里的rad[i+k]=rad[i]-k=min(rad[i]-k,rad[i-k]).
2. rad[i]-k>rad[i-k]
如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分在黑色的部分里面,根据定义,很容易得出:rad[i+k]=rad[i-k].
为了方便下文,这里的rad[i+k]=rad[i-k]=min(rad[i]-k,rad[i-k]).
根据上面两种情况,可以得出结论:当rad[i]-k!=rad[i-k]的时候,rad[i+k]=min(rad[i]-k,rad[i-k]).
3. rad[i]-k==rad[i-k]
如图,通过和第一种情况对比之后会发现,因为青色的部分没有超出黑色的部分,所以即使橙色的部分全等,
也无法像第一种情况一样引出矛盾,因此橙色的部分是有可能全等的,但是,根据已知的信息,我们不知道橙色的部分是多长,
因此就把i指针移到i+k的位置,j=rad[i-k](因为它的rad值至少为rad[i-k]),等下次循环的时候再做了.
对于长度为偶数的回文子串,一种比较好的方法就是在原来的串中每两个字符之间加入一个特殊字符,再做.
如:aabbaca,把它变成a#a#b#b#a#c#a,这样的话,无论原来的回文子串长度是偶数还是奇数,现在都变成奇数了.
此算法用到了一个Rad[]数组的定义,Rad[i]表示回文的半径,即最大的j满足s[i-rad[i],i-1]=s[i+1,i+rad[i]]。
很明显,找到了所有的rad[i],就求出了所有的长度为奇数的回文子串,至于偶数的怎么求,最后再讲.
假设现在求出了rad[1..i-1],现在要求后面的rad值,并且通过前面的操作,得知了当前字符i的rad值至少为j.
现在通过试图扩大j来扫描,求出了rad[i].再假设现在有个指针k,从1循环到rad[i],试图通过某些手段来
求出[i+1,i+rad[i]]的rad值.
根据定义,黑色的部分是一个回文子串,两段红色的区间全等,因为之前已经求出了rad[i-k],所以直接用它.
有3种情况:
1. rad[i]-k<rad[i-k]
如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分超过了黑色的部分,所以rad[i+k]肯定至少为rad[i]-k,
即橙色的部分.那橙色以外的部分就不是了吗?这是肯定的.因为如果橙色以外的部分也是回文的,那么根据青色和红色部分的
关系,可以证明黑色部分再往外延伸一点也是一个回文子串,这肯定不可能,因此rad[i+k]=rad[i]-k.为了方便下文,
这里的rad[i+k]=rad[i]-k=min(rad[i]-k,rad[i-k]).
2. rad[i]-k>rad[i-k]
如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分在黑色的部分里面,根据定义,很容易得出:rad[i+k]=rad[i-k].
为了方便下文,这里的rad[i+k]=rad[i-k]=min(rad[i]-k,rad[i-k]).
根据上面两种情况,可以得出结论:当rad[i]-k!=rad[i-k]的时候,rad[i+k]=min(rad[i]-k,rad[i-k]).
3. rad[i]-k==rad[i-k]
如图,通过和第一种情况对比之后会发现,因为青色的部分没有超出黑色的部分,所以即使橙色的部分全等,
也无法像第一种情况一样引出矛盾,因此橙色的部分是有可能全等的,但是,根据已知的信息,我们不知道橙色的部分是多长,
因此就把i指针移到i+k的位置,j=rad[i-k](因为它的rad值至少为rad[i-k]),等下次循环的时候再做了.
对于长度为偶数的回文子串,一种比较好的方法就是在原来的串中每两个字符之间加入一个特殊字符,再做.
如:aabbaca,把它变成a#a#b#b#a#c#a,这样的话,无论原来的回文子串长度是偶数还是奇数,现在都变成奇数了.
#include <iostream> #include <string> using namespace std; const int maxnum = 1000000; int rad[maxnum*2+1]; int max(int a, int b) { if (a > b) return a; else return b; } int min(int a, int b) { if (a < b) return a; else return b; } int manacher(string str) { string s; int n = str.size(); int ans = 0; int i, j, k; for (i = 0; i < n; i++) { s += '#'; s += str[i]; } s += '#'; n = (n << 1) + 1; i = 0; j = 1; while (i < n) { while (i - j >= 0 && i + j < n && s[i - j] == s[i + j]) j++; rad[i] = j - 1; k = 1; while (k <= rad[i] && rad[i] - k != rad[i - k]) { rad[i + k] = min(rad[i - k], rad[i] - k); k++; } i += k; j = j - k; } for (i = 0; i < n; i++) ans = max(ans, rad[i]); return ans; } int main() { string str; cin >> str; cout << manacher(str) << endl; return 0; }
相关文章推荐
- 编程之法:关于最长回文子串 | Manacher 算法详解
- Manacher 算法详解:O(n) 复杂度求最长回文子串
- Manacher(回文算法代码及其详解)
- Canny边缘检测算法原理及其VC实现详解
- Canny边缘检测算法原理及其VC实现详解(二)
- O(n)回文子串(Manacher)算法
- Canny边缘检测算法原理及其VC实现详解(一)
- Manacher算法--O(n)回文子串算法
- HDU 3068 最长回文 manacher 算法,基本上是O(n)复杂度
- 串子串Manacher算法: O(n)时间求字符串的最长回文子串
- Canny边缘检测算法原理及其VC实现详解(一)
- 最长回文子串(Longest Palindrome Substring)--Manacher算法O(n)
- Manacher算法--O(n)回文子串算法
- Manacher算法处理字符串回文
- HDU 3068 最长回文(manacher O(n)求回文算法)
- 最大算法【Manacher模板】HDU 3068——求最长回文子串
- O(n)回文子串(Manacher)算法
- Canny边缘检测算法原理及其VC实现详解(二)
- O(n)回文子串(Manacher)算法
- Canny边缘检测算法原理及其VC实现详解(二)