解题报告
题目 :http://acm.hdu.edu.cn/showproblem.php?pid=3068
题目大意 :求给定串的最长回文子串(2009多校题目)
算法 :manacher;
思路 :枚举每个点向左向右扩展,看最远能扩展到哪儿.但是普通的枚举是n^2的,肯定超时。现在我们想kmp或扩展kmp一样,给字符串定义一个nex数组,nex[i]表示以i为中心最远能向右扩展的长度,使得s[i – nex[i] + 1……. i + nex[i]- 1]形成的回文。然后我们利用这个数组,在O(n)的时间内求出每个i的nex[i]。在其他算法中,奇数回文和偶数回文经常给我们带来麻烦,这个算法中,我们第一步要进行的是将每个字符后边(包括开头)加入一个字符(不在串儿的字符集中就行),一般用’#”. 这样就都转换为了奇数的情况。
例如
改为
剩下的就是在我们知道了nex[0]…….nex[i – 1]后 如何求nex[i]; p记录前i-1个字符中以某个字符id为中心最远能向右扩展到的位置。
代码:
void Get_nex(){
}
if(p > i) nex[i]= MIN(nex[2 * id - i], p -i);
这是代码中最核心的一句话,它的作用是让我们在以i为中心向左右扩展时,尽量减少重复的比较,当p > i 时会有两种情况。设j = 2 *id – i
第一:当以i为中心的回文和以j为中心的回文都在以id为中心的回文中时。
第二:当以i为中心的回文或以j为中心的回文不在以id为中心的回文中时。
或:
c a b a c a b q c
这时以i为中心的最长回文不一定大于nex[i],但是由于回文的对称性,他最小是p-i(nex[j]大时)或nex[j]。
收获以经验 :又学到了一种新算法,happy。
提交情况 :accepted 1次
AC code:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;
typedef int I64;
typedef double real;
#define MAX(a, b) ((a) > (b) ?(a) : (b))
#define MIN(a, b) ((a) < (b) ?(a) : (b))
#define mem(a, b) memset(a, b, sizeof(a))
#define fup(i, a, b) for(i = a; i < b; ++i)
#define fdn(i, a, b) for(i = a; i > b; --i)
#define INF 210000000
I64 Gcd(I64 a, I64 b){return b ? Gcd(b, a% b) : a;}
I64 Lcm(I64 a, I64 b){return a / Gcd(a, b)* b; }
#define MAXL 110100
char s[MAXL], tem[MAXL * 2];
I64 L1, L2, nex[MAXL* 2];
void Get_tem(){
}
void Get_nex(){
}
int main(){
}