【POJ3974】Palindrome Manacher、模板题 裸题

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

题解:Manacher裸题、函数

Manacher:spa

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

思想:code


首先咱们维护一个id指针表示能够用id指针更新i的【状态】。string

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

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

             有时回文子串长度是偶数,就会以一个不存在的'空'为中心,因此每每在两个字符间加一个分割字符,好比ab这个串就每每会被改为~a~b~,而后Manacher中有一个延展的步骤须要比较当前回文的两边,进行延展,为了不边界条件,因此咱们每每会在前面再加一个字符,变成¥~a~b~。for循环


分析转移:class

    咱们能够发现对于一个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;
}