博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
最长回文子串:Manacher算法[转]
阅读量:4299 次
发布时间:2019-05-27

本文共 2467 字,大约阅读时间需要 8 分钟。

原文:

背景

给定一个字符串,求出其最长回文子串。例如:

s="abcd";   //最长回文长度为 1s="ababa";  //最长回文长度为 5s="abccb";  //最长回文长度为 4,即 bccb

以上问题的传统思路大概是,遍历每一个字符,以该字符为中点向两边查找。其时间复杂度为 O(n2) ,很不高效。而Manacher算法可以把时间复杂度提升到 O(n)

算法过程分析

由于回文分为偶回文(比如bccb)和奇回文(比如bcacb),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,具体做法是,在字符串首尾,及字符间各插入一个字符(前提这个字符未出现在串里)。

举个例子:s="abbahopxpo",转换为s_new="#a#b#b#a#h#o#p#x#p#o#"。如此,s里起初有一个偶回文abba和一个奇回文opxpo,被转换为#a#b#b#a##o#p#x#p#o#,长度都转换成了奇数。

定义一个辅助数组int p[],其中p[i]表示以i为中心的最长回文的半径,例如:

i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
s_new[i] # a # b # b # a # h # o # p # x # p #
p[i] 1 2 1 2 5 2 1 2 1 2 1 2 1 2 1 4 1 2 1

可以看出,p[i] - 1正好是原字符串中最长回文串的长度。

接下来的重点就是求解p数组,如下图:

这里写图片描述

设置两个变量mx,idmx代表以id为中心的最长回文的右边界,也就是mx = id + p[id]。假设我们现在求p[i],也就是以i为中心的最长回文半径,如果i < mx,则p[i]的值可基于以下三种情况得出:

1. j的回文串有一部分在id的之外

这里写图片描述

上图中,黑线为id的回文,i,j关于id对称,红线为j的回文。此时p[i] = mx - i,即紫线。那么p[i]还可以更大么?答案是不可能!见下图:

这里写图片描述

假设右侧新增的紫色部分是p[i]可以增加的部分,那么根据回文的性质,a == d ,也就是说id的回文不仅仅是黑线,而是黑线+两条紫线,矛盾,所以假设不成立,故p[i] = mx - i,不可以再增加一分。

2. j回文串全部在id的内部

这里写图片描述

此时p[i] = p[j],那么p[i]还可以更大么?答案亦是不可能!见下图:

这里写图片描述

假设右侧新增的红色部分是p[i]可以增加的部分,那么根据回文的性质,a == b,也就是说j的回文应该再加上a,b,矛盾,所以假设不成立,故p[i] = p[j],也不可以再增加一分。

3. j回文串左端正好与id的回文串左端重合

这里写图片描述

根据代码,此时p[i] = p[j]p[i] = mx - i,并且p[i]还可以继续增加。

代码实现

#include 
#include
using namespace std;// Manacher 算法// 功能:给定一个字符串,求它的最长回文子串长度int manacher(string str) { int max_loc = str.size() * 2; // 初始化字符串 string s_new(max_loc + 1, '#'); for (int ii = str.size() - 1; ii >= 0; --ii) { s_new[2 * ii + 1] = str[ii]; } // 遍历字符串 vector
p(max_loc + 1, 1); int max_len = 1; for (int ii = 1, jj, id = 0, mx = 1; ii < s_new.size(); ++ii) { if (ii - id >= mx) { for (; ii + p[ii] < s_new.size() && ii - p[ii] >= 0; ++p[ii]) { if (s_new[ii + p[ii]] != s_new[ii - p[ii]]) break; } id = ii; mx = p[ii]; max_len = max(mx, max_len); } else { jj = id - (ii - id); if (jj - p[jj] == id - mx) { for (p[ii] = id + mx - ii; ii + p[ii] < s_new.size() && ii - p[ii] >= 0; ++p[ii]) { if (s_new[ii + p[ii]] != s_new[ii - p[ii]]) break; } id = ii; mx = p[ii]; max_len = max(mx, max_len); } else if (jj - p[jj] < id - mx) { p[ii] = id + mx - ii; } else { p[ii] = p[jj]; } } } return max_len - 1;}

转载地址:http://lhsws.baihongyu.com/

你可能感兴趣的文章
力扣题解-123. 买卖股票的最佳时机 III(动态规划)
查看>>
java中ThreadLocal类的使用
查看>>
java中数组长度为零和为空的区别
查看>>
图解eclipse 查看原始类出现The jar file rt.jar has no source attachment
查看>>
JVM堆内存设置原理
查看>>
约瑟夫问题(java实现)
查看>>
Java 中int、String的类型转换
查看>>
java实现9大排序算法
查看>>
一句话总结java23种设计模式
查看>>
静态表查找
查看>>
乐观锁的一种实现方式——CAS
查看>>
JAVA线程间通信的几种方式
查看>>
IDEA中怎么新建package包,只有directory选项
查看>>
django admin 增加查看权限
查看>>
django后台加载从15秒优化到1秒的过程小记
查看>>
Python将繁体文件批量重命名简体中文文件
查看>>
chrome不显示Django-suit左侧菜单栏
查看>>
Python区间库python-intervals
查看>>
django admin 登录用户名密码错误提示
查看>>
python3 AttributeError: 'function' object has no attribute 'func_name'
查看>>