回文字符串--manacher算法
来源:互联网 发布:淘宝女装销售话术 编辑:程序博客网 时间:2024/05/16 13:55
回文字符串--manacher算法
回文串定义: “回文串” 是一个正读和反读都一样的字符串, 比如 “level ” 或者 “noon”等等就是回文串。回文子串,顾名思义,即字符串中满足回文性质的子串。
经常有一些题目围绕回文子串进行讨论,比如 HDOJ_3068_最长回文,求最长回文子串的长度。 朴素算法是依次以每一个字符为中心向两侧进行扩展, 显然这个复杂度是 O(N^2)的, 关于字符串的题目常用的算法有 KMP、 后缀数组、 AC 自动机, 这道题目利用扩展 KMP可以解答,其时间复杂度也很快 O(N*logN)。但是,今天笔者介绍一个专门针对回文子串的算法,其时间复杂度为 O(n),这就是 manacher 算法。
大家都知道,求回文串时需要判断其奇偶性,也就是求 aba 和 abba 的算法略有差距。
然而, 这个算法做了一个简单的处理,很巧妙地把奇数长度回文串与偶数长度回文串统一考虑, 也就是在每个相邻的字符之间插入一个分隔符, 串的首尾也要加, 当然这个分隔符不能再原串中出现,一般可以用‘#’或者‘$’等字符。例如:
原串:abaab
新串:#a#b#a#a#b#
这样一来,原来的奇数长度回文串还是奇数长度,偶数长度的也变成以‘#’为中心的奇数回文串了。
接下来就是算法的中心思想,用一个辅助数组 P 记录以每个字符为中心的最长回文半径, 也就是 P[i]记录以 s[i]字符为中心的最长回文串半径。 P[i]最小为 1, 此时回文串为 s[i]本身。
我们可以对上述例子写出其 P 数组,如下
新串: # a # b # a # a # b #
P[ ] : 1 2 1 4 1 2 5 2 1 2 1
我们可以证明 P[i]-1 就是以 Str[i]为中心的回文串在原串当中的长度。
证明:
1、显然 L=2*P[i]-1 即为新串中以 Str[i]为中心最长回文串长度。
2、以 Str[i]为中心的回文串一定是以#开头和结尾的,例如“#b#b#”或“#b#a#b#”
所以 L 减去最前或者最后的‘#’字符就是原串中长度的二倍,即原串长度为(L-1)/2,化简得 P[i]-1。得证。
依次从前往后求得 P 数组就可以了,这里用到了 DP(动态规划)的思想,也就是求 P[i]的时候,前面的 P[]值已经得到了,我们利用回文串的特殊性质可以进行一个大大的优化。
我先把核心代码贴上:
for(i=1;i<n;i++){if(MaxId>i){p[i]=Min(p[2*id-i],MaxId-i);}else{p[i]=1;}while(a[i+p[i]]==a[i-p[i]]){p[i]++;}if(p[i]+i>MaxId){MaxId=p[i]+i;id=i;}}
定义数组p[i]表示以i为中心的(包含i这个字符)回文串半径长
将字符串s从前扫到后for(int i=0;i<strlen(s);++i)来计算p[i],则最大的p[i]就是最长回文串长度,则问题是如何去求p[i]?
由于s是从前扫到后的,所以需要计算p[i]时一定已经计算好了p[1]....p[i-1]
假设现在扫描到了i+k这个位置,现在需要计算p[i+k]
定义maxlen是i+k位置前所有回文串中能延伸到的最右端的位置,即maxlen=p[i]+i;//p[i]+i表示最大的
分两种情况:
1.i+k这个位置不在前面的任何回文串中,即i+k>maxlen,则初始化p[i+k]=1;//本身是回文串
然后p[i+k]左右延伸,即while(s[i+k+p[i+k]] == s[i+k-p[i+k]])++p[i+k]
2.i+k这个位置被前面以位置i为中心的回文串包含,即maxlen>i+k
这样的话p[i+k]就不是从1开始
由于回文串的性质,可知i+k这个位置关于i与i-k对称,
所以p[i+k]分为以下3种情况得出
//黑色是i的回文串范围,蓝色是i-k的回文串范围,
贴上hdu-3068代码以供详细参考:
#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;const int maxn=110010;char str1[maxn],str[2*maxn];int p[2*maxn];void Init(char *s){ int i,j,len; len = strlen(s); str[0]='$';str[1]='#'; for(i=0,j=2;i<len;i++,j++) { str[j]=s[i]; str[++j]='#'; } str[j]='\0'; //for(int i=0;i<j;i++) // cout << str[i] <<' ';}int manacher(char *s){ int i,len,ans,maxlen,id; len = strlen(s); ans = id = maxlen = 0; for(i=1;i<len;i++) { if(maxlen>i) p[i]=min(p[2*id-i],maxlen-i); else p[i]=1; while(s[i+p[i]] == s[i-p[i]]) p[i]++; if(p[i]+i>maxlen) { maxlen = p[i]+i; id = i; } if(p[i]>ans) ans = p[i]; } return ans-1;}int main(){ while(scanf("%s",str1)!=EOF) { int k; memset(str,0,sizeof(str)); memset(p,0,sizeof(p)); Init(str1); k=manacher(str); printf("%d\n",k); } return 0;}
- 回文字符串--manacher算法
- Manacher算法处理字符串回文
- 最长回文字符串(manacher算法)
- Manacher算法处理字符串回文
- 最长回文字符串--manacher算法
- 最长回文字符串 manacher算法
- manacher算法(最长回文字符串)
- 最长回文字符串 manacher算法
- 字符串专题:Manacher算法处理字符串回文
- 最长回文字符串(manacher算法)
- 【转载】最长回文字符串(manacher算法)
- 回文字符串—— Manacher算法
- String——回文字符串manacher算法
- Manacher 字符串回文查询算法说解
- Manacher O(n) 回文字符串查找算法
- java manacher算法计算最长回文字符串
- 字符串--manacher算法(回文串匹配)
- 最长回文字符串(Manacher算法)
- JS创建类和对象
- √如何改善无线wifi信号质量
- WPF应用Binding之数据有效性检测
- 数据结构之栈
- 干货_草根创业者自媒体明星之路
- 回文字符串--manacher算法
- QML BOOK 第八章 Particle Simulations
- QT中PRO文件写法的详细介绍,很有用,很重要!
- 在C++中使用sqlite的示例(从下载、编译、工程建立、配置、测试代码编写全方位介绍)
- bind peeking--绑定变量窥视
- Android JNI入门第三篇——jni头文件分析
- JAVA中堆和栈的区别
- arc engine 闪烁定位 高亮显示 放大图形
- Jackson 框架,轻易转换JSON