傳送門
描述
如果一個字符串正着讀和倒着讀是一樣的,則稱它是迴文的。
給定一個長度爲N的字符串S,求他的最長迴文子串的長度是多少。
輸入格式
輸入將包含最多30個測試用例,每個測試用例佔一行,以最多1000000個小寫字符的形式給出。
輸入以一個以字符串“END”(不包括引號)開頭的行表示輸入終止。
輸出格式
對於輸入中的每個測試用例,輸出測試用例編號和最大回文子串的長度(參考樣例格式)。
每個輸出佔一行。
輸入樣例:
abcbabcbabcba
abacacbaaaab
END
輸出樣例:
Case 1: 13
Case 2: 6
這個題是要我們求最大的長度,所以我們可以用manacher,也可以字符串哈希,這裏主要分享字符串哈希做法
首先我們可以知道長度最小是1,所以我們就枚舉答案,對於一個字符串是否是迴文串我們只需要計算他正反的哈希值是否相同就行了
所以我們首先計算正反的哈希值,然後我們在從頭開始枚舉迴文串的中心,然後先判斷當前的答案區間是否是迴文,如果是則繼續往兩邊找
然後找到邊界之後我們更新我們的答案,而如果當前區間不是迴文那我們直接跳過枚舉下一個點,這樣O(n)就能找到區間
而如果要輸出區間的話就可以再用一個變量來標記我們是在以哪個點爲中點的時候找到的迴文串最大長度,這樣迴文串就能確定了
AC代碼如下:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int M=1e6+10,P=131;
ull h1[M*2],p[M*2],h2[M*2];
char s1[M],s2[2*M];
int main() {
int k=0;
while(cin>>s1&&strcmp(s1, "END")) {
cout << "Case " << ++k << ": ";
int cnt=1;
s2[1] = '#';
int n=strlen(s1);
for(int i=0; i<n; i++) s2[++cnt] = s1[i], s2[++cnt] = '#';
n = cnt, s2[cnt + 1] = '\0';
p[0]=1;
for(int i = 1; i <=n; i++) h1[i] = h1[i-1] * P + s2[i], p[i] = p[i - 1] * P;
for(int i = n; i >=1; i--) h2[i] = h2[i+1] * P + s2[i];
int ans=0,l;
for(int i = 1; i <= n; i++) {
l = ans;
if(i + l > n || i - l < 1) break;
if(h1[i+l]-h1[i-1]*p[l+1]!=h2[i-l]-h2[i+1]*p[l+1]) continue;
while(s2[i+l+1]==s2[i-l-1]&&i+l+1<=n&&i-l-1>=1)l++;
ans=max(ans,l);
}
cout<<ans<<endl;
}
return 0;
}