【POJ3974】Palindrome Manacher、模板题 裸题

题意:最长回文子串长度、

题解:Manacher裸题、

Manacher:

本质:重复利用之前的信息来快速更新新的状态、

思想:


首先我们维护一个id指针表示可以用id指针更新i的【状态】。

【状态】:p[i]表示以i为中心的最长回文半径。

        (一般i+p[i]是第一个'超出回文',即拓展到这里不成回文,下文也按照这个习惯来写)

             有时回文子串长度是偶数,就会以一个不存在的'空'为中心,所以往往在两个字符间加一个分割字符,比如ab这个串就往往会被改成~a~b~,然后Manacher中有一个延展的步骤需要比较当前回文的两边,进行延展,为了避免边界条件,所以我们往往会在前面再加一个字符,变成¥~a~b~。


分析转移:

    我们可以发现对于一个id指针,i会在id的另一侧有一个对称点j,

Ⅰ. 如果以j为中心的回文子串没有超出以id为中心的回文子串,那么以i为中心的回文子串显然与以j为中心的回文子串相同。

Ⅱ. 否则,因为超出部分不能保证相等,所以回文子串需要被截到以id为中心的回文子串的边界来更新p[i]。

那么p[i]就可以有一个初始值p[i]=min(p[id*2-i],mx-i);


    然后我们只是给以i为中心的回文子串确定了一个最小回文半径,它并不一定是p[i]的真实值。

    所以我们再暴力往两边拓展。


    那么我们如何确定一个比较好的id呢?

    可以发现,我们需要p[i]在暴力拓展前的值尽量大,那么我们就需要以id为中心的回文子串能容纳的以i为中心的回文子串的限度尽量大,即id+p[id]尽量大。

    于是我们再定义一个mx表示id+p[id]来判定当前最优的id。


时间复杂度分析

首先我们发现manacher函数中有一个for循环,其中只有一个while可能存在时间复杂度的拓宽。

分析:while循环的作用——>暴力拓展p[i],而这对应上文的Ⅱ. ,可以分析得到如果p[i]增加,那么k+p[k](k∈[1,i])的最大值,即mx肯定就等于 i+p[i],也即id会被更新成i。

而mx最大不会超过len,即进入while循环并p[i]++的次数不会超过len。

而len约为原len<<1 ,所以可以证明manacher的时间复杂度是O(n) 的。。。


贴代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1001000
using namespace std;
char ts[N],s[N<<1];
int p[N<<1];
int manacher()
{
	int len=strlen(ts),i;
	s[0]='&',s[1]='^';
	for(i=1;i<=len;i++)s[i<<1]=ts[i-1],s[i<<1|1]='^';
	len++,len<<=1;
	int ret=0,mx=0,id=0;
	for(i=1;i<len;i++)
	{
		if(mx>i)p[i]=min(p[id*2-i],mx-i);
		else p[i]=1;
		while(s[i-p[i]]==s[i+p[i]])p[i]++;
		if(mx<p[i]+i)mx=p[i]+i,id=i;
		ret=max(ret,p[i]);
	}
	return ret-1;
}

int main()
{
//	freopen("test.in","r",stdin);
//	freopen("my.out","w",stdout);
	int i,j,k;
	int id=0;
	while(scanf("%s",ts),ts[0]!='E'&&ts[0]!='O'&&ts[0]!='F')printf("Case %d: %d\n",++id,manacher());
	return 0;
}

你可能感兴趣的:(palindrome,Manacher,poj3974)