前言
对于找一个字符串的最长回文子串有很多钟方法,但是复杂度往往都不尽如人意,比如暴力复杂度为O(n3),动态规划和中心扩散法做这个题的复杂度为O(n2),而马拉车的复杂度为O(n)。所以一般情况下我们都会用马拉车来做或者也有人用后缀数组来做,反正看个人习惯吧。
正文
例题(hdu 3068)
Problem Description
给出一个只由小写英文字符a,b,c…y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c…y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
Sample Input
aaaa
abab
Sample Output
4
3
马拉车关键部分如下
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
当 mx - i > P[j] 的时候,以 S[j] 为中心的回文子串包含在以 S[id] 为中心的回文子串中,由于 i 和 j 对称,以 S[i] 为中心的回文子串必然包含在以 S[id] 为中心的回文子串中,所以必有 P[i] = P[j],其中 j = 2id - i,因为 j 到 id 之间到距离等于 id 到 i 之间到距离,为 i - id,所以 j = id - (i - id) = 2id - i。
当 P[j] >= mx - i 的时候,以 S[j] 为中心的回文子串不一定完全包含于以 S[id] 为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以 S[i] 为中心的回文子串,其向右至少会扩张到 mx 的位置,也就是说 P[i] = mx - i。至于 mx 之后的部分是否对称,就只能老老实实去匹配了,这就是后面紧跟到 while 循环的作用。
AC代码如下
#include<bits/stdc++.h>
using namespace std;
void Manacher(string s) {
string t = "$#";
for (int i = 0; i < s.size(); ++i) {
t += s[i];
t += "#";
}
vector<int> p(t.size(), 0);
int mx = 0, id = 0, resLen = 0, resCenter = 0;
for (int i = 1; i < t.size(); ++i) {
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
while (t[i + p[i]] == t[i - p[i]]) ++p[i];
if (mx < i + p[i]) {
mx = i + p[i];
id = i;
}
if (resLen < p[i]) {
resLen = p[i];
resCenter = i;
}
}
cout<<resLen-1<<endl;
return ;
//return s.substr((resCenter - resLen) / 2, resLen - 1);
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);
string s;
while(cin>>s)
Manacher(s);
return 0;
}