题目背景算法
给定一个字符串str,若子串s是回文串(如aba, abba),则称s为str的回文子串,如今须要求出str的最长回文子串数组
解法ide
1.枚举法
spa
这种解法比暴力法好一点,就是遍历字符串每一位str[i]:blog
(1)首先若是是aba型,那就从i出发往两边扩张,看str[i-1]跟str[i+1]是否相等,进而扩张到str[i-k]跟str[i+k],直到str[i-(k+1)]跟str[i+(k+1)]不相等,注意这个扩张的前提的不越界,这样子就能获得最长长度是1+2*k字符串
(2)再者还得讨论abba型,那就从i,i+1分别出发往两边扩张,看str[i-1]跟str[i+1+1]是否相等,进而扩张到str[i-k]跟str[i+1+k],直到str[i-(k+1)]跟str[i+1+(k+1)]不相等,注意这个扩张的前提的不越界,这样子就能获得最长长度是2+2*kget
取(1)和(2)中较大的值,最后综合全部的i,取出最最大的值就是最长回文子串it
这种解法的时间复杂度是O(n2),可是还存在一种线性的时间复杂度算法class
2.Manacher算法遍历
首先须要对字符串进行预处理,目的是要统一aba和abba的解法,作法是在每一个字符串间隙和头尾都加上#
如 a b b a --> # a # b # b # a #
这样生成新的字符串S,它必定是个奇数,经过有字母的字符位能够计算aba的状况,经过#的字符位能够计算abba的状况,这样就很好把两种状况统一了
定义一个数组P[i],来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),即单边的范围
接下来须要考虑的是已经P[0],P[1]...P[i-1]的状况下怎么获得P[i]
从0...i-1中取一个id,使P[id]+id最大,记作mx=P[id]+id,这个mx显然是以前的最长回文子串可以到达的最右端的范围
如今就考察i有没有落在[0,mx)区间,若是没落在那就说明以前的P[0],P[1]...P[i-1]都没法帮助计算P[i],须要用枚举法计算,若是落在,那就说明P[0],P[1]...P[i-1]至少是有价值的,进一步能够分类讨论:
设i关于id的对称点为j,知足j=2*id-i,设mx关于id的对称点my,知足my=2*k-mx
若(1)mx-i>P[j],也即j-my>P[j],这种状况就是回文里嵌回文,因此直接可得P[i]=P[j]
(2)mx-i<P[j],也即j-my<P[j],这种状况是部分回文里嵌回文,没法直接获得P[i],但至少能够得知P[i]>mx-i,这样也能够省下很多计算,直接从mx-i开始用枚举法往外扩张求得最后值
最后贴一下代码
void Manacher(const char* str) { int size = strlen(str); int N = 2*size+1; int* p = new int[N]; int mx = 1; int id = 0; p[0] = 1; for(int i = 1; i < N; i++) { if(i < mx) p[i] = min(p[2*id-i], mx-i); else p[i] = 1; while((i != p[i]) && (((i+p[i]) % 2 == 1) || (str[(i+p[i])/2-1] == str[(i-p[i])/2-1]))) p[i]++; if(mx < i + p[i]) { mx = i + p[i]; id = i; } } Print(str); Print(p, N); delete[] p; }