1. 程式人生 > 實用技巧 >Manacher演算法求解最長迴文串

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