BZOJ 2565 Mancher 算法求 最长双回文串

http://www.lydsy.com/JudgeOnline/problem.php?id=2565

求出最长的子串可以分成两个回文串

先处理出以每个位置为中心的最长回文子串,然后我是求了两个这样的数组left[] right[],分别表示某位置往左最长的回文子串(以该位置结尾),右边的雷同

需要线性扫两遍。。。

处理这两个数组的时候细节问题整了半天,所幸最后还是整出来了。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn= 200010;
namespace M {
	int n;
	struct node {
		int a,b;
		int cen;
		node() {}
		node(int _a,int _b):a(_a),b(_b){};
		bool operator < (const node&cmp) const {
			return b < cmp.b;
		}
	}in[maxn];
	vector<pair<int,int> > P[maxn] ;
///	P[i]记录以i为中心的所有的最长回文子串(其实最多只有两个,奇偶性。。) 
	int left[maxn] , right[maxn];
	bool ji[maxn];
	void solve()
	{
		memset(ji,false,sizeof(ji));
		int mx = 0;
		for(int i = 0; i < n; i++)mx = max(mx,in[i].b);
		for(int i = 1; i <= mx; i++) P[i].clear();
		for(int i = 0; i < n; i++)
		{
			in[i].cen= in[i].a+in[i].b >> 1;
			P[in[i].cen].push_back(make_pair(in[i].a,in[i].b));
		}
        int pt = 1;
        for(int i = 1; i <= mx; i++)
        {
        	for(; pt < i; pt++)
        	{		
        		int a = P[pt][0].first;
        		int b = P[pt][0].second;
        		if(b>=i)
        		{
        			ji[i] = true;
        			break;
				}
				if(P[pt].size()==1) continue;
				a = P[pt][1].first;
				b = P[pt][1].second;
				if(b>=i)
				{
					break;
				}
			}
			left[i] = pt;
		}
		for(int i = 1; i <= mx; i++)
		{
			left[i] = max(1,(i - left[i])*2+ji[i]);
		}
		pt = mx;
		memset(ji,false,sizeof(ji));
		for(int i = mx; i >= 1; i--)
		{
			right[i] = i;
 	        for(; pt >= i; pt--)
        	{	
        	    int a, b;
			    if(P[pt].size() > 1)	
			    {
             	   a = P[pt][1].first;
				   b = P[pt][1].second;
				   if(a<=i)
 				   {
                      right[i] = pt + 1;
					  break;
				   }
			    }
        		a = P[pt][0].first;
        		b = P[pt][0].second;
        		if(a<=i)
        		{
					right[i] = pt;	
        			ji[i] = true;
        			break;
				}			
			}
		}
		for(int i = 1; i <= mx; i++)
		{
			right[i] = max(1,(right[i] - i)*2+ji[i]);
		} 
		int ans = 0;
		for(int i = 1; i < mx; i++)
		{
               ans = max(ans,left[i]+right[i+1]);
		}
		printf("%d\n",ans);
	}
}
struct Mancher {
	char str[maxn];//start from index 1
	int p[maxn];
	char s[maxn];
	int n;
	void checkmax(int &ans,int b){
		if(b>ans) ans=b;
	}
	inline int min(int a,int b){
		return a<b?a:b;
	}
	void kp(){
		int i;
		int mx = 0;
		int id;
		for(i=1; i<n; i++){
			if( mx > i )
				p[i] = min( p[2*id-i], p[id]+id-i );
			else
				p[i] = 1;
			for(; str[i+p[i]] == str[i-p[i]]; p[i]++) ;
			if( p[i] + i > mx ) {
				mx = p[i] + i;
				id = i;
			}
		}
	}
	void pre()
	{
		int i,j,k;
		n = strlen(s);
		str[0] = '$';
		str[1] = '#';
		for(i=0;i<n;i++)
		{
			str[i*2 + 2] = s[i];
			str[i*2 + 3] = '#';
		}
		n = n*2 + 2;
		str[n] = 0;
	}
	void solve() // 求出所有的最长回文子串所在的区间
	{
		int & tot = M::n;
		tot = 0;
		for(int i = 2; i < n; i++)
		{
			if(i%2&&p[i]==1) continue;
			if(i%2)
			{
				M::in[tot++] = M::node(i/2-p[i]/2+1,i/2+p[i]/2);
				//printf("%d %d\n",i/2-p[i]/2+1,i/2+p[i]/2);
			}
			else 
			{
				M::in[tot++] = M::node(i/2-(p[i]/2-1),i/2+(p[i]/2-1));
				//printf("%d %d\n",i/2-(p[i]/2-1),i/2+(p[i]/2-1));
			}
		}
	}
}task1;
int main()
{
	while( scanf("%s", task1.s) !=EOF )
	{
		task1.pre();
		task1.kp();
		task1.solve();
		M::solve();
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值