题解:
这个题目如果单独拿出来就是一道裸的Manacher算法,但是这里希望我们强化一下Hash算法,所以我就写了两个版本的。
Manacher:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#define ll long long
#define INF 1e12
#define inf -1e12
#define PII pair<int,int>
#define ull unsigned long long
#define ios std :: ios :: sync_with_stdio(false)
using namespace std;
const int maxn = 1e6 + 19;
char s[maxn];
char s_new[2 * maxn];
int p[2 * maxn];
int init()
{
int len = strlen(s);
s_new[0] = '$';
s_new[1] = '#';
int j = 2;
for(int i = 0;i < len;i++){
s_new[j++] = s[i];
s_new[j++] = '#';
}
s_new[j] = '\0';
return j;
}
int manacher()
{
int len = init();
int max_len = 1,id,mx = 0;
for(int i = 1;i <= len;i++){
if(i < mx) p[i] = min(p[2 * id - i],mx - i);
else p[i] = 1;
while(s_new[i - p[i]] == s_new[i + p[i]]) p[i] ++;
if(mx < i + p[i]){
id = i;
mx = i + p[i];
}
max_len = max(max_len,p[i] - 1);
}
return max_len;
}
int main()
{
int cnt = 1;
while(~scanf("%s",s)){
if(s[0] == 'E') break;
cout << "Case " << cnt << ": " << manacher() << endl;
cnt++;
}
return 0;
}
Hash:
判断一个字符串是不是回文串,我们如果从Hash值的角度入手的话,我们就需要维护一个前缀和后缀的Hash数组,只要把这个字符串一分为二,分别比较其前缀值和后缀值判断是否相等即可。但是这里要注意的是,我们对于奇回文和偶回文的处理是不一样的。
我们假设当前的回文串是一个奇回文,那么肯定他的中心位置是一个字符,我们假设当前的位置是i
,那么就有
H
(
i
−
p
,
i
−
1
)
=
H
(
i
+
1
,
i
+
p
)
H(i - p,i - 1) = H(i + 1,i + p)
H(i−p,i−1)=H(i+1,i+p)。其中p
是回文串可以延申的最大长度,那么最后得到的回文串的长度是:
l
e
n
=
2
p
+
1
len = 2p + 1
len=2p+1
我们再考虑偶回文的情况,此时他的中心位置就不再是一个具体的字符了,而变成了一个“缝隙”,此时我们考虑缝隙左边的字符,就有:
H
(
i
−
p
,
i
)
=
H
(
i
+
1
,
i
+
p
+
1
)
H(i - p,i ) = H(i + 1,i + p + 1)
H(i−p,i)=H(i+1,i+p+1),此时的回文串的长度为:
l
e
n
=
2
(
p
+
1
)
len = 2(p + 1)
len=2(p+1)
现在我们只需要预处理出当前字符串的前缀后缀Hash数组(注意处理后缀的时候尽量不要reverse
,这样在后面处理起来会麻烦)然后二分答案,查找p
的大小即可。
需要注意的两点:
- 对于p的查找千万小心 p = 1 p = 1 p=1的情况,此时对偶回文的查找应该有特判。
- 处理后缀和尽量不要
reverse
,把从小到大换成从大到小即可,但是在求Hash值的时候一定要注意它的变化。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<queue>
#include<bitset>
#include<list>
#define ll long long
#define INF 1e12
#define inf -1e12
#define PII pair<int,int>
#define ull unsigned long long
#define ios std :: ios :: sync_with_stdio(false)
using namespace std;
const int maxn = 1e6 + 11;
const int P = 131;
ull p[maxn * 2],H1[maxn],H2[maxn];
void init(string s,int len)
{
memset(p,0,sizeof(p));
memset(H1,0,sizeof(H1));
memset(H2,0,sizeof(H2));
p[0] = 1;
H1[0] = s[0] - 'a' + 1;
for(int i = 1; i <= len; i++) p[i] = p[i - 1] * P;
for(int i = 1; i < len; i++) H1[i] = P * H1[i - 1] + (s[i] - 'a' + 1);
H2[len - 1] = s[len - 1] - 'a' + 1;
for(int i = len - 2;i >= 1;i--) H2[i] = H2[i + 1] * P + (s[i] - 'a' + 1);
}
ull Hash1(int i,int j) //normal
{
return H1[j] - H1[i - 1] * p[j - i + 1];
}
ull Hash2(int i,int j) //reverse
{
return H2[i] - H2[j + 1] * p[j - i + 1];
}
int Judge(string s,int len)
{
int ans = 0;
for(int i = 0;i < len;i++){
int l = 0, r = len,mid;
while(l < r){
mid = (l + r + 1) >> 1;
if(i - mid < 0 || mid + i >= len) r = mid - 1;
else if(Hash1(i - mid,i - 1) == Hash2(i + 1,i + mid)) l = mid;
else r = mid - 1;
}
ans = max(ans,l << 1 | 1);
l = 0,r = len,mid = 0;
while(l < r){
mid = (l + r + 1) >> 1;
if(i - mid < 0 || mid + i >= len) r = mid - 1;
else if(Hash1(i - mid,i) == Hash2(i + 1,i + mid + 1)) l = mid;
else r = mid - 1;
}
ans = max(ans,l != 0 ? (l + 1) << 1 : 1);
}
return ans;
}
int main()
{
ios;
string s;
int cnt = 0;
while(cin >> s){
if(s == "END") break;
int len = s.length();
init(s,len);
int ans = Judge(s,len);
cout << "Case " << ++ cnt << ": " << ans << endl;
}
return 0;
}