天梯赛l2-008最长对称子串(马拉车/manacher,25分代码)

l2-008

e5bacb05b9874d7e9626a216a45fb1c6.png

前言 

该题属于manacher模板题,我会在下面的思路中讲解manacher。

题目

对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。

输入格式:

输入在一行中给出长度不超过1000的非空字符串。

输出格式:

在一行中输出最长对称子串的长度。

输入样例:

Is PAT&TAP symmetric?

输出样例:

11

思路

由于奇数长度回文串与偶数长度回文串求解方法不同,为了方便起见,在字符串每个字符后面都添加一个~,在字符串最开头添加一个~。设原字符串长度为len,处理后字符串长度为2*len+1,于是只需要求解奇数长度字符串就行了。

关于下面AC代码第18行的

s[++cnt] = '~';

这是用于处理最后一个字符,防止越界的。(不懂的话,此处可在看完这篇题解后再重新看)

下面是关键。

设p[i]为以字符串第i个字符对称中心最长回文串半径。//这里非常关键。

p[i]是包括i的,比如长度为3的回文串,p[i]==2;长度为1的回文串,p[i]==1。

r已知最长回文串最右端的下标。

mid已知最长回文串中心点(对称中心)

i当前处理的下标。

34c9b7af19184062816e67a2775130c5.png

如果 i <= r :

i处于以mid为中心的最长回文串里面,

i关于mid对称的点2*mid-i也处于以mid为中心的最长回文串里面,

所以p[i]一般来说是等于p[2*mid-i]的;

什么时候不一般呢?p[i]必须满足一个条件:i+p[i]-1<=r,以i为中心的回文串最右边不能超过当前最长回文串的最右边,变换一下→p[i]<=r-i+1

也就是说当p[2*mid-i]>r-i+1时,不一般,此时p[i]=r-i+1;

p[i] = min(p[2 * mid - i], r - i + 1);

然后暴力扩展

while (s[i - p[i]] == s[i + p[i]])++p[i];

 

如果p[i]>=r-i+1,即i+p[i]>r那么就应该将i设为新的最长回文串的中心,mid改为i

如果 i > r :

p[i]赋值为1,暴力扩展

while (s[i - p[i]] == s[i + p[i]])++p[i];

如果p[i]>=r-i+1,即i+p[i]>r那么就应该将i设为新的最长回文串的中心,mid改为i

其余的细节我就不讲了,如果其余的细节不懂,说明你需要重新看题解。

manacher的思想在于假设当前真,蓝桥杯的飞机降落和manacher有异曲同工之妙。

讲完了。

AC代码

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2002;
string original;//这个单词是原样的意思
char s[N];
int len, cnt, r, mid, ans;
int p[N];
int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    //getline用于输入一行字符串。
	getline(cin, original, '\n');//'\n'是默认参数,可以不写,写了是为了加强记忆这里可以加参数。
	len = original.length();
	s[++cnt] = '~';//从1开始,习惯。
	for (int i = 0; i < len; ++i)s[++cnt] = original[i], s[++cnt] = '~';
	s[++cnt] = '~';//这里是用于处理最后一个字符
	for (int i = 1; i < cnt; ++i)
	{
		if (i <= r)p[i] = min(p[2 * mid - i], r - i + 1);
		else p[i] = 1;
		while (s[i - p[i]] == s[i + p[i]])++p[i];
		if (i + p[i] > r)r = i + p[i] - 1, mid = i;
		ans = max(ans, p[i]);
	}
	cout << ans - 1;
	return 0;
}

千里之行,始于足下。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值