manacher算法(求最长子回文)
首先介绍两个变量概念:
回文右边界(R):能扩到的右边界。
回文中心(C):扩到最右边界时的回文中心
算出任意位置i所能得到的回文半径一共有两种大的可能。
1.(可能性一)回文右边界在左边,暴力扩
2.回文右边界在右边或在回文右边界上,作回文中心的对称点 i'
(可能性二) i' 的回文半径彻底在左右回文串边界里面---------i的回文半径不用扩,一定和 i' 一样
(可能性三)i' 的回文半径彻底不在左右回文串边界里面---------i的回文半径就是i->r这段间隔
(可能性四)i'的回文半径正好在左右回文串边界上--------i的回文半径从i->r不用验,后面的要自己验。
//Manacher算法:查找一个字符串中最长回文串
#include<bits/stdc++.h>
using namespace std;
//填充源字符串
vector<char> manacherstring(vector<char> a)
{
vector<char> str;
str.push_back( '#' );
for( int i=0;i<a.size();i++ )
{
str.push_back( a[i] );
str.push_back( '#' );
}
return str;
}
vector<char> randomstring()
{
vector<char> str;
for(int i=0;i<10000;i++)
{
str.push_back( rand()%26+'a' );
}
return str;
}
//暴力向两边扩
int simplehuiwen(vector<char> vec)
{
int res=1;
int len=vec.size();
for(int i=0;i<len;i++)
{
int gap=1;
int cnt=1;
while( i-gap>=0&&i+gap<=len-1 )
{
//cout<<vec[i-gap]<<" "<<vec[i+gap]<<endl;
if( vec[i-gap]==vec[i+gap] )
{
cnt++;
gap++;
}
else
{
break;
}
}
res=max( cnt,res );
}
return res-1;
}
int manacher(vector<char> vec)
{
//定c为回文中心,r为回文半径的右边界
int c=-1;
int r=-1;
int res=1;
int veclen=vec.size();
//记录每个位置能扩到的右边界
int* pArr=new int[veclen];
memset( pArr,0,sizeof(pArr) );
for( int i=0;i<veclen;i++ )
{
//先将能扩到的先扩到,不然就从自己开始扩
pArr[i] = r>i ? min( pArr[2*c-i],r-i ) : 1;
//每种情况都尝试向外扩
while( i-pArr[i]>=0&&i+pArr[i]<veclen )
{
if( vec[i-pArr[i]]==vec[i+pArr[i]] )
{
pArr[i]++;
}
else
{
break;
}
}
//如果发生扩,就去记录第一次扩到r位置的中心
if( i+pArr[i]>r )
{
r=i+pArr[i];
c=i;
}
res=max( res,pArr[i]);
}
return res-1;
}
int main()
{
srand( (unsigned)time(NULL) );
//vector<char> s={'1','2','2','1'};
//cout<<manacher(manacherstring(s));
for(int i=0;i<1000;i++)
{
vector<char> s=randomstring();
if( manacher( manacherstring( s ) ) != simplehuiwen( manacherstring( s ) ) )
{
for(int i=0;i<s.size();i++)
{
cout<<s[i];
}
cout<<endl;
cout<<"sad!"<<endl;
}
}
cout<<"good!"<<endl;
}
求在字符串最后添加最少字符使之成为一个回文串。
只有扩右边界时能碰到原字符串有边界就停止
//暴力向两边扩
int simplehuiwen(vector<char> vec)
{
int res=1;
int len=vec.size();
for(int i=0; i<len; i++)
{
int gap=1;
int cnt=1;
while( i-gap>=0&&i+gap<=len-1 )
{
//cout<<vec[i-gap]<<" "<<vec[i+gap]<<endl;
if( vec[i-gap]==vec[i+gap] )
{
if(i+gap==len-1)
{
return (2*i -len+1 )/2;
}
cnt++;
gap++;
}
else
{
break;
}
}
}
}
int manacher(vector<char> vec)
{
//定c为回文中心,r为回文半径的右边界
int c=-1;
int r=-1;
int res=1;
int veclen=vec.size();
//记录每个位置能扩到的右边界
int* pArr=new int[veclen];
memset( pArr,0,sizeof(pArr) );
for( int i=0; i<veclen; i++ )
{
//先将能扩到的先扩到,不然就从自己开始扩
pArr[i] = r>i ? min( pArr[2*c-i],r-i ) : 1;
//每种情况都尝试向外扩
while( i-pArr[i]>=0&&i+pArr[i]<veclen )
{
if( vec[i-pArr[i]]==vec[i+pArr[i]] )
{
pArr[i]++;
}
else
{
break;
}
}
//如果发生扩,就去记录第一次扩到r位置的中心
if( i+pArr[i]>r )
{
r=i+pArr[i];
c=i;
}
if( r==veclen )
{
return ( 2*c-veclen+1 )/2;
}
}
}

浙公网安备 33010602011771号