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;
        }

    }

}

  

 

posted @ 2020-08-14 23:54  肉松松松松  阅读(127)  评论(0)    收藏  举报