「Luogu P1210」回文检测 解题报告

题面

这是一道诡异的黄题

居然让你求一串吧啦吧啦的东西中 字母(大小写)最长的回文串的长度,还要输出完整的串 吐血

思路:

保持淡定,我们啥都不会,就会Manacher,那就用Manacher大法!

1、把字符串处理成只有字母(去皮)

2、把字符串中插入#号(掺假)

3、Manacher跑一跑最长回文串长度(市场检查)

4、求出最长长度后再重新代入原来的字符串(打回原形!妖孽!)

然后,就完了~

就是去皮的时候,顺便记录一下字符的位置,这样重新代入时就比较方便

并且由于字符串有很多行,读入时就要用getchar(),直到EOF为止

似乎Noip2018普及T1有人因为这个WA?

Code:

#include<bits/stdc++.h>
#define M 20010
using namespace std;
struct node{
	char c;
	int id;
}a[M<<1];
char st[M],s[M<<1];
int l1,l2,len,ans,res;
int p[M<<1];
bool check(char c)
{
	return (c>='a'&&c<='z')||(c>='A'&&c<='Z');
}
void init()
{
	int i;
	for(i=0;i<l1;i++)
		if(check(st[i]))
		{
			a[l2].c=(st[i]>='a'&&st[i]<='z')? st[i]:st[i]-'A'+'a';
			a[l2++].id=i;//记录编号(位置)
		}
	s[0]='!';
	s[1]='#';
	for(i=0;i<l2;i++)
	{
		s[i*2+2]=a[i].c;
		s[i*2+3]='#';
	}
	len=l2*2+2;
	s[len]='?';
	return;
}
void manacher()//大法不解释
{
	int id=0,mx=0;
	for(int i=0;i<len;i++)
	{
		if(i<mx)
			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(i+p[i]>mx)
		{
			id=i;
			mx=i+p[i];
		}
	}
	return;
}
void Print(int l,int r)
{
	if(l&1)//由扩展后的s数组变回a数组
		l=(l-3)/2;
	else
		l=(l-2)/2;
	if(r&1)
		r=(r-3)/2;
	else
		r=(r-2)/2;
	for(int k=a[l].id;k<=a[r].id;k++)
		printf("%c",st[k]);
	return;
}
int main()
{
	int i;
	char c=getchar();//读入
	while(c!=EOF)
	{
		st[l1++]=c;
		c=getchar();
	}
	init();//初始化
	manacher();//大法
	for(i=0;i<len;i++)//求最长
		ans=max(ans,p[i]);
	for(i=0;i<len;i++)//求第一次出现的位置
		if(p[i]==ans)
			break;
	ans--;
	printf("%d
",ans);
	Print(i-ans+1,i+ans-1);//重新代入
	return 0;
}
原文地址:https://www.cnblogs.com/hovny/p/10172954.html