浅谈Manacher算法与扩展KMP之间的联系
首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解。网上解法颇多。时间复杂度也不尽同样,这里列述几种常见的解法.
解法一
latex=\dpi{100}&space;\fn_jvn&space;N^2">
\dpi{100}&space;\fn_jvn&space;N^2" title="N^2" alt="">
。加上每次推断须要latex=\dpi{100}&space;\fn_jvn&space;O(n)">
\dpi{100}&space;\fn_jvn&space;O(1)" title="O(1)" alt="">
.bool check(string &S, int left, int right)
{
while (left < right && S[left] == S[right])
++left, --right;
return left >= right;
}
int solution(string &S)
{
int ans = 0;
for (int i = 0; i < S.size(); ++i)
for (int j = i; j < S.size(); ++j)
if (check(S, i, j))
ans = max(ans, j - i + 1);
return ans;
}
解法二
现如果得知S[i....j]是S的一个回文子串,那么,我们相同能够得到S[i+1.....j-1]也是S的一个回文字串,换句话说,我们能够通过已知的状态求解出未知状态。现定义dp[i][j]表示S以i为起点,j为终点的子串是否为回文,状态转移方程也非常easy想到:
\dpi{100}&space;\fn_jvn&space;dp[i][j]&space;=&space;i&space;<=&space;j&space;-&space;2&space;?&space;(S[i]&space;==&space;S[j]" title="dp[i][j] = i <= j - 2 ?
(S[i] == S[j]" alt="">
&&\dpi{100}&space;\fn_jvn&space;dp[i&space;+&space;1]&space;==&space;dp[j&space;-&space;1])&space;:&space;S[i]&space;==&space;S[j]" title="dp[i + 1] == dp[j - 1]) : S[i] == S[j]" alt="">
latex=\dpi{100}&space;\fn_jvn&space;N^2">
latex=\dpi{100}&space;\fn_jvn&space;O(N^2)">
int solution(string &S)
{
vector<vector<bool> > dp(2, vector<bool>(S.size(), false));
int ans = 0;
for (int i = S.size() - 1; i >= 0; --i)
{
for (int j = i; j < S.size(); ++j)
{
dp[i & 1][j] = i <= j - 2 ? (S[i] == S[j] && dp[(i + 1) & 1][j - 1]) : S[i] == S[j];
if (dp[i & 1][j])
ans = max(ans, j - i + 1);
}
}
return ans;
}
解法三
latex=\dpi{100}&space;\fn_jvn&space;N">
latex=\dpi{100}&space;\fn_jvn&space;O(N)">
latex=\dpi{100}&space;\fn_jvn&space;O(N^2)">
\dpi{100}&space;\fn_jvn&space;O(N^2)" title="O(N^2)" alt="">
,空间复杂度int solution(string &S)
{
const int n = S.size();
int ans = 0;
for (int i = 0; i < n; ++i)
{
//for the odd case
for (int j = 0; (i - j >= 0) && (i + j < n) && S[i - j] == S[i + j]; ++j)
ans = max(ans, j << 1 | 1);
//for the even case
for (int j = 0; (i - j >= 0) && (i + 1 + j < n) && S[i - j] == S[i + 1 + j]; ++j)
ans = max(ans, 2 * j + 2);
}
return ans;
}
解法四
\dpi{100}&space;\fn_jvn&space;O(n)" title="O(n)" alt="">
。这里,我们能够通过利用字符串的hash来减少时间复杂度(注:不熟悉字符串hash的朋友,能够參考下这篇博客点击打开链接,整理的非常具体)。
如果当前推断的是以i为中点偶数长度的最长回文,对于随意一个长度k,如果S[i latex=\dpi{100}&space;\fn_jvn&space;O(lgn)"> \dpi{100}&space;\fn_jvn&space;O(lgn)" title="O(lgn)" alt=""> latex=\dpi{100}&space;\fn_jvn&space;O(nlgn)">
- k + 1....i]的hash值与S[i + 1.....i + k]的hash值不同,那么以i为中点的最长回文子串的长度必然小于2 * k,因此。能够通过该条件进行二分。这样就能在
。
const int BASE = 131, N = 1e+6 + 7;
typedef unsigned long long ULL;
//rec: record forward direction hash value
//rRec:record backward direction hash value
//P: record power of BASE
ULL rec[N], rRec[N], P[N];
int Bin(int len, int end, int rEnd, int __len)
{
int l = 1, r = len;
while (l <= r)
{
int mid = l + (r - l) / 2;
ULL lHash = rec[end] - (end - mid >= 0 ? rec[end - mid] : 0) * P[mid];
ULL rHash = rRec[rEnd] - (rEnd + mid < __len ? rRec[rEnd + mid] : 0) * P[mid];
if (lHash ^ rHash)
r = mid - 1;
else
l = mid + 1;
}
return r;
}
int solution(char *S)
{
const int len = strlen(S);
P[0] = 1ULL;
//calculate power of BASE
for (int i = 1; i < =len; ++i)
P[i] = P[i - 1] * 131;
rec[0] = S[0], rRec[len - 1] = S[len - 1];
//calculate the string <span style="font-family:Microsoft YaHei;">hash </span>value
for (int i = 1, j = len - 2; i < len; ++i, --j)
rec[i] = rec[i - 1] * BASE + S[i], rRec[j] = rRec[j + 1] * BASE + S[j];
int ans = 0;
for (int i = 0; i < len; ++i)
{
int tmp;
//for the even case
tmp = Bin(min(i + 1, len - i - 1), i, i + 1, len);
ans = max(ans, tmp << 1);
//for the odd case
tmp = Bin(min(i, len - i - 1), i - 1, i + 1, len);
ans = max(ans, tmp << 1 | 1);
}
return ans;
}
上述代码有两个地方须要说明一下:1.无符号长整型溢出时,编译器会自己主动取模 2.关于计算P数组。假设是单case,P数组的求解能够放到solution函数中,假设是多case,P数组的求解必须放到外面,由于P数组仅仅用计算一次就能够了.此种解法。能跑过POJ
3974和hdu 3068,感兴趣的朋友能够试试这样的解法.
解法五
Manacher算法
- i <= right: 先计算i关于center的对称点i' = 2 * center - i,依据回文串的对称性,从框左边left...i'和i...right是一致的。假设P[i']的值能把i + P[i']的值限定在框里,那么P[i] = P[i'],由于框里的东西已经比較过了。比如源串为babcbabcbaccba,如今要计算P[13]值,例如以下图所看到的:
i = 13关于center的对称点是i' = 9。将[i' - P[i']......i + P[i]]子串取出(这里为了便于叙述,先如果i' - P[i'] >L)。得到例如以下的图:
通过对照上图能够发现,以i'为中点的最长回文子串S[8..10]相应着S[12...14],也就是说,S[i' - P[i']...i' + P[i']]与S[i - P[i']...i + P[i']]一定是相等的(注:此处的前提条件是i' - P[i'] >L),并且P[i]一定等于P[i'],由于S[i + P[i'] + 1] 一定不等于S[i - P[i'] - 1],这在求P[i']时,就已经比較过了。当i' - P[i'] <= L时,能够得到S[L...2 * i' -
L]一定是回文子串,而S[L...2 * i' - L]恒等于S[2 * i - R....R]。此时,P[i]的值至少是R - i。而大于right部分的,都是没有比較过的,所以仅仅能以i为中点,以R - i + 1为半径向两边扩展。结合i' - P[i'] > L和i' - P[i'] <= L的情况,能够发现P[i]的值至少等于min(P[i'], R - i),所以,在i < right的情况下。使P[i] = min(P[i'], R - i),然后以i为中心,P[i]为半径,向两边扩展。并更新对应的center和right值就可以. - i > right: 这样的情况下,仅仅能以i为中心,向两边扩展,并更新对应的center和right值。
复杂度分析
const int N = 1e+6 + 7;
char orign[N << 1];
int P[N << 1];
int Manacher(char *S)
{
int len = strlen(S);
S[len << 1] = '#', S[len << 1 | 1] = '\0';
for (int i = len - 1; i >= 0; --i)
S[i << 1 | 1] = S[i], S[i << 1] = '#';
int center = 0, right = 0, ans = 0;
len <<= 1;
for (int i = 0; i <= len; ++i)
{
P[i] = i <= right ? min(P[2 * center - i], right - i) : 0;
while (i - P[i] - 1 >= 0 && i + P[i] + 1 <= len && S[i - P[i] - 1] == S[i + P[i] + 1])
++P[i];
if (i + P[i] > right)
right = i + P[i], center = i;
ans = max(ans, P[i]);
}
return ans;
}
应用一:回文子串个数
如果如今须要求解以i为中心、长度为奇数的回文子串数量。仅仅须要找到以i为中心、长度为奇数的最长回文子串的长度值,然后将长度值加一除2。即为所求的解。偶数的处理方式一样。而在求解最长回文子串的长度时,计算出来的P[i]值。就已经计算出了以源串全部点为中心、长度各自是偶数和奇数的最长回文子串的长度。仅仅须要线性遍历一遍P[i]数组,将(P[i]
+ 1) / 2的值累加,就是S的回文子串的个数。
应用二:扩展KMP
现计算P[i],如果P[1...i - 1]都已经计算好了,设定right为max(P[x] - 1 + x)(1 <= x < i)。left为取到right值时的x值。(1)当right >= i时,通过已经计算出来的P[1..i-1]值,我们可知S[left....right] = S[0...right
- left]。找到i的位置相当于S串的开头的位置:i' = i - left,假设i + P[i'] <= right。那么非常easy得出P[i] = P[i']。假设i + P[i'] > right,那么P[i]的值至少为right - i + 1,综上两个情况,易知P[i]值至少为min(P[i']。right - i + 1),然后暴力比較,并更新对应的left和right。因为right的值仅仅能添加n次。所以该算法是。
n)。相同能够利用上述的方法在
浅谈Manacher算法与扩展KMP之间的联系的更多相关文章
- 【字符串算法2】浅谈Manacher算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 字符串算法2:Manacher算法 问题:给出字符串S(限制见后)求出最 ...
- 浅谈Manacher算法
Manacher manacher是一种\(O(n)\)求最长回文子串的算法,俗称马拉车(滑稽) 直接步入正题 首先可以知道的是:每一个回文串都有自己的对称中心,相应的也有自己的最大延伸长度(可以称之 ...
- 浅谈分词算法(4)基于字的分词方法(CRF)
目录 前言 目录 条件随机场(conditional random field CRF) 核心点 线性链条件随机场 简化形式 CRF分词 CRF VS HMM 代码实现 训练代码 实验结果 参考文献 ...
- 浅谈分词算法(5)基于字的分词方法(bi-LSTM)
目录 前言 目录 循环神经网络 基于LSTM的分词 Embedding 数据预处理 模型 如何添加用户词典 前言 很早便规划的浅谈分词算法,总共分为了五个部分,想聊聊自己在各种场景中使用到的分词方法做 ...
- 浅谈分词算法(3)基于字的分词方法(HMM)
目录 前言 目录 隐马尔可夫模型(Hidden Markov Model,HMM) HMM分词 两个假设 Viterbi算法 代码实现 实现效果 完整代码 参考文献 前言 在浅谈分词算法(1)分词中的 ...
- 浅谈分词算法基于字的分词方法(HMM)
前言 在浅谈分词算法(1)分词中的基本问题我们讨论过基于词典的分词和基于字的分词两大类,在浅谈分词算法(2)基于词典的分词方法文中我们利用n-gram实现了基于词典的分词方法.在(1)中,我们也讨论了 ...
- 浅谈Manacher
\(Manacher\)是由一个叫做\(Manacher\)的人发明的能在\(O(n)\)时间内找出一个字符串长度最长的回文子串的算法. 由于偶回文串形如\(abba\)这样的不好找对称中心,所以我们 ...
- 浅谈Tarjan算法
从这里开始 预备知识 两个数组 Tarjan 算法的应用 求割点和割边 求点-双连通分量 求边-双连通分量 求强连通分量 预备知识 设无向图$G_{0} = (V_{0}, E_{0})$,其中$V_ ...
- 浅谈聚类算法(K-means)
聚类算法(K-means)目的是将n个对象根据它们各自属性分成k个不同的簇,使得簇内各个对象的相似度尽可能高,而各簇之间的相似度尽量小. 而如何评测相似度呢,采用的准则函数是误差平方和(因此也叫K-均 ...
随机推荐
- 触发TreeView的TreeNodeCheckChanged事件
这个事件不会主动postback,需要手动写javascript触发.对网上找到的方法做了些改进,增加UpdatePanel,以免页面不停的刷.这里就不考虑性能神马的了,因为既然项目已经允许选择使用T ...
- 用DOM实现文章采集-HtmlAgilityPack实现html解析
Html Agility Pack 是CodePlex 上的一个开源项目.它提供了标准的DOM API 和XPath 支持! 下载地址:http://htmlagilitypack.codeplex. ...
- 用java写bp神经网络(一)
根据前篇博文<神经网络之后向传播算法>,现在用java实现一个bp神经网络.矩阵运算采用jblas库,然后逐渐增加功能,支持并行计算,然后支持输入向量调整,最后支持L-BFGS学习算法. ...
- 使用Eclipse创建Hibernate工程
创建一个java project项目,加入hibernate的jar包和数据库驱动包,并引入到项目.
- CSS3重要内容翻译
以上是废话 1.3 此处未完全确认,相较于css3和css3的选择器,区别包括: 基础定义改变(选择器.选择器组,简单选择器等),特别的,作为css2中简单选择器,如今被成为简单选择器序列,“简 ...
- jquery mobile 栅格化
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- Android 多线程:使用Thread和Handler
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分 ...
- C++ 性能剖析 (一)
C++ 性能剖析 (一) 性能问题也不是仅仅用“技术”可以解决的,它往往是架构,测试,假设等综合难题.不过,对于一个工程师来说,必须从小做起,把一些“明显”的小问题解决.否则的话积小成多,千里堤坝,溃 ...
- Nginx源码研究七:nginx的location指令分析
在nginx的配置文件nginx.conf中,我们在配置server的时候,会配置一下location指令,这个location指令是提供给用户来配置对于符合指令的http请求,采用该指令内部的处理方 ...
- hdu 1241 Oil Deposits(DFS求连通块)
HDU 1241 Oil Deposits L -DFS Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & ...