最高效的方法是Manacher算法,时间复杂度为O(N),在2N步内即可找到最长回文子串
Manacher算法的基本思想是用一个O(N)的数组P来存储每一个元素为中心时回文子串的长度
为了提高效率,还特别增加了两种措施:
1.对原字符串的首尾以及每个元素间都增加一个特殊字符(如#),使得新字符串的长度都变成奇数位
2.计算P[i]时会考虑到之前计算过的回文长度,设i之前最长回文子串位置为curcenter,则对应的回文子串长度为P[curcenter],右边界即为right=curcenter+P[curcenter]。根据i和right的关系,有三种情况:
(1) i>=right,说明当前元素i超出了curcenter的回文串范围,此时需要重新计算i的回文串长度,并把curcenter置为i
(2) i<right,且i相对于curcenter的对称点i'的子串长度为P[i'],i+P[i']也不超过right,说明i的子串仍然在right范围内,此时P[i]=P[i'],不需要计算
(3) i<right,但i+P[i']超过了right,说明i的子串超出了right范围,此时P[i]至少等于[i,right]区间的长度,right以外的回文串长度需要额外计算
代码:
#include <iostream> #include <string> using namespace std; int findMaxDasLen( const string& str , int& center , int& sublen ) {//计算str的最长回文子串长度并返回 int len = str.length(); if( len <= 1 ) return len; string newstr; for( string::const_iterator it=str.begin() ; it!=str.end() ; it++ ) {//每个字符间加上# newstr += "#"; newstr += (*it); } newstr += '#'; int newLen = newstr.length();//加完#后的字符串长度 int* P = new int[ newLen ];//用来存储以每个元素为中心的最长回文串长度 P[0] = 1;//第一个字符是#,最大回文长度为1 int curcenter = 0;//初始中心位置是0 for( int pos=1 ; pos<newLen ; pos++ ) { int right = curcenter + P[curcenter];//右边界坐标 int lpos = 2*curcenter-pos;//左对称点坐标 if( pos>=right ) P[pos] = 1;//pos在右边界外,需要从头计算回文串长度 else if( P[lpos] <= right-pos ) P[pos] = P[lpos];//pos的回文串全在right以左 else P[pos] = right-pos;//pos的回文串部分在right以左 while( pos+P[pos]<newLen && pos-P[pos]>=0 && newstr[pos+P[pos]]==newstr[pos-P[pos]] ) P[pos]++;//检查pos为中心的回文串 if( pos + P[pos] > right ) curcenter = pos;//回文串超过右边界,更新中心位置 } int maxpos=0; for( int i=1 ; i<newLen ; i++ ) { if( P[i] > P[maxpos] ) maxpos = i; } sublen = P[maxpos]-1;//原字符串中的最大回文串长度 center = (maxpos)/2;//原字符串中的最大回文串中心坐标 delete[] P; return sublen; } int main() { string str1 = "abaaba"; string str2 = "aaaabaa"; string str3 = "acacdas"; int center=0; int sublen=0; cout << findMaxDasLen( str1 , center , sublen ) << endl; cout << str1.substr( center-(sublen)/2 , sublen ) << endl; cout << findMaxDasLen( str2 , center , sublen ) << endl; cout << str2.substr( center-(sublen)/2 , sublen ) << endl; cout << findMaxDasLen( str3 , center , sublen ) << endl; cout << str3.substr( center-(sublen)/2 , sublen ) << endl; system("pause"); }