Manacher算法求解最长回文串

推荐的参考博文

根据博主的介绍,我写出了下面的C++代码

#include<bits/stdc++.h>
using namespace std;

string Pre(string s)
{//将s转化成ManacherString 
	int lenth = s.size();
	string line="#";
	for(int i=0;i<lenth;i++)
	{
		line+=s[i];
		line+="#";
	}
	return line;
}

string Manacher(string aim)
{
	aim = Pre(aim);//字符串预处理
	int lenth = aim.size();
	int* temp = new int[lenth];
	for(int i=0;i<lenth;i++)temp[i]=1;//初始化回文半径数组 
	//下面是核心代码
	int p=0,C=-1,R=-1;//p当前访问字符,C最右回文边界的对称中心,R最右回文边界
	while(p<lenth)
	{
		if(p>R)//要移动的位置p在R的右侧
		{
			int s = p;
			while(s<lenth&&2*p-s>=0&&aim[s]==aim[2*p-s])s++;//以p为中心向两侧扩张
			C=p;R=s-1;temp[p]=s-p;
		}
		else// if(p<=R) 第二种情形:要移动的位置p不在R的右侧
		{
			int p2 = 2*C-p;//p2是p1以C为对称中心的对称点
			int cL = 2*C-R;//cL是以C为对称中心的回文子串的左边界
			int pL = p2-temp[p2]+1;//pL是以p2为对称中心的回文子串的左边界
            //第二情形的三种情况
            if(cL==pL)
            {
                int s = R;
				while(2*p-s>=0&&s<lenth&&aim[s]==aim[2*p-s])s++;
				C=p;R=s-1;temp[p]=s-p;
            }
            else temp[p] = (cL<pL)? temp[p2]:(R-p+1);
		}
		p++;//千万不要忘了这个
	}//while
    //下面查找答案
	int maxnum = -1;
	int maxp=-1;
	string ans="";//用于存储将要返回的最长回文串
    //下面检测最长回文串的中心
	for(int i=0;i<lenth;i++)
	{
		if(temp[i]>maxnum)
		{
			maxnum = temp[i];
			maxp = i;
		}
	}
	for(int i=maxp-maxnum+1;i<maxp+maxnum;i++)
	{//整理最长回文串
		if(aim[i]!='#')ans+=aim[i];
	}
	delete[] temp;//删除长度记录数组
	return ans;
}


int main()
{
	string aim = "acbbcbds";
	cout<<Manacher(aim)<<endl;
	return 0;
}//输出的是cbbc

先不要急着抄写,下面我们对核心代码进行压缩并使其模板化


string Pre(string s)
{//将s转化成ManacherString 
	int lenth = s.size();
	string line="#";
	for(int i=0;i<lenth;i++)
	{
		line+=s[i];
		line+="#";
	}
	return line;
}

int NormalExtend(int center,int lenth,string& aim,int& C,int& R)
{//对称中心center,字符串长度lenth,字符串引用aim,最右回文边界的对称中心C,最右回文边界R
	int s = max(center,R);
	while(s<lenth && 2*center-s>=0 && aim[s]==aim[2*center-s]) s++;
	C=center;
	R=s-1;
	return s-center;//该值会被赋给标记数组temp[center]
}

string Manacher(string aim)
{
	aim = Pre(aim);//字符串预处理
	int lenth = aim.size();
	int* temp = new int[lenth];
	for(int i=0;i<lenth;i++)temp[i]=1;//初始化回文半径数组

	//下面是核心代码的压缩版本
	int p=0,C=-1,R=-1;//p当前访问字符,C最右回文边界的对称中心,R最右回文边界
	while(p<lenth)
	{
		int p2 = 2*C-p;//p2是p1以C为对称中心的对称点
		int cL = 2*C-R;//cL是以C为对称中心的回文子串的左边界
		int pL = p2-temp[p2]+1;//pL是以p2为对称中心的回文子串的左边界
		if(p>R || (p<=R&&cL==pL)) temp[p] = NormalExtend(p,lenth,aim,C,R);
		else temp[p] = (cL<pL)? temp[p2]:(R-p+1);
		p++;
	}

    //下面查找答案
	int maxnum = -1;
	int maxp=-1;
	string ans="";//用于存储将要返回的最长回文串
    //下面检测最长回文串的中心
	for(int i=0;i<lenth;i++)
	{
		if(temp[i]>maxnum)
		{
			maxnum = temp[i];
			maxp = i;
		}
	}
	for(int i=maxp-maxnum+1;i<maxp+maxnum;i++)
	{//整理最长回文串
		if(aim[i]!='#')ans+=aim[i];
	}
	delete[] temp;//删除长度记录数组
	return ans;
}

OK

posted @ 2020-07-28 16:57  SavenNeer  阅读(98)  评论(0编辑  收藏  举报