前言
该题属于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是当前处理的下标。
如果 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;
}
千里之行,始于足下。