动态规划
#include <bits/stdc++.h>
using namespace std;
int dp[1010][1010];
int main() {
string s;
getline(cin,s);
size_t ans=0;
for(size_t len=0;len<s.size();++len){
for(size_t i=0,j=0;i+len<s.size();++i){
j=i+len;
if(len==0) dp[i][j]=1;
else if(len==1) dp[i][j]=(s[i]==s[j]);
else dp[i][j]=(s[i]==s[j]&&dp[i+1][j-1]);
ans=(dp[i][j]&&j-i>ans?j-i:ans);
}
}
cout<<ans+1;
}
字符哈希
字符串 s
的
[
0
,
i
]
[0,i]
[0,i] 子串哈希的值满足公式:h[i]=(h[i-1]*P+s[i])%MOD
,其中,P=1e7+19
,MOD=1e9+7
,是两个大质数。
[
i
,
j
]
[i,j]
[i,j] 子串的哈希值满足公式hval(h,i,j)=((h[j]-h[i]*pow_p[j-i+1])%MOD+MOD)%MOD
,其中 pow_p[i]
表示
P
i
P^i
Pi。欲证明,展开 h[j]
即可。
于是可以获得 s
对应的 h1[s]
,然后把 s
翻转获得 h2[s]
。对奇子串
[
i
−
k
,
i
+
k
]
[i-k,i+k]
[i−k,i+k](回文半径为
k
k
k,回文中心为
i
i
i),判断是否为回文串,相当于判断 s
的
[
i
−
k
,
i
]
[i-k,i]
[i−k,i] 和
[
i
,
i
+
k
]
[i,i+k]
[i,i+k] 字串是否相等,即 hval(h1,i-k,i)==hval(h2,len-1-(i-k),len-1-i)
;对偶子串
[
i
−
k
+
1
,
i
+
k
]
[i-k+1,i+k]
[i−k+1,i+k](回文半径为
k
k
k,回文中心为
i
i
i 右边的空隙),相当于判断 s
的子串
[
i
−
k
+
1
,
i
]
[i-k+1,i]
[i−k+1,i] 和
[
i
+
1
,
i
+
k
]
[i+1,i+k]
[i+1,i+k] 是否相等,即 hval(h1,i-k,i)==hval(h2,len-1-(i+k),len-1-(i+1))
。于是可以枚举回文中心,对回文半径二分。
接下来一个问题就是,这里的二分是 lower_bound 问题还是 upper_bound 问题?对比两种问题:
- lower_bound: [ m i d ] < t a r g e t [mid]<target [mid]<target 时, l ← m i d + 1 l \leftarrow mid+1 l←mid+1; [ m i d ] ≥ t a r g e t [mid] \ge target [mid]≥target 时, r ← m i d r \leftarrow mid r←mid
- upper_bound: [ m i d ] ≤ t a r g e t [mid] \le target [mid]≤target 时, l ← m i d + 1 l \leftarrow mid+1 l←mid+1; [ m i d ] > t a r g e t [mid] > target [mid]>target 时, r ← m i d r \leftarrow mid r←mid
(详细可以见这篇文章。)
上面判断返回 false
时,说明回文半径 mid
大于目标回文半径,需要改变右边界。如果是 lower_bound 问题,此时搜索的是第一个大于等于目标回文半径的值, r=mid
不能严格缩小搜索范围,r=mid-1
则可能排除掉目标回文半径(当搜索域不存在目标回文半径时),于是,说明这个问题是upper_bound问题,搜索的是第一个大于目标回文半径的值,即搜索完成后 l=k+1
。
但是这道题,搜索域中一定能找到目标值(奇数情况为1,偶数情况为0)上述讨论中用 lower_boud 思想看似也可以,此时判断失败时 r=mid-1
,判断成功时 l=mid
,相应的返回值也要改变。但是,由于 mid=(l+r)/2
的向左归一特性,最后会导致死循环,所以这条路走不通。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
using LL=long long;
const LL P=1e7+19,MOD=1e9+7;
LL pow_p[MAXN],h1[MAXN],h2[MAXN];
string s;
int GetSubStrH(LL h[],int i,int j){
if(i==0) return h[j];
return ((h[j]-h[i-1]*pow_p[j-i+1])%MOD+MOD)%MOD;
}
int BS(int i,bool is_even){
int sz=s.size();
// 二分查找第一个大于回文半径的 mid,upper_bound 问题。
if(!is_even){
int l=0,r=min(i,sz-1-i)+1;
while(l<r){
int mid=(l+r)/2;
int hval1=GetSubStrH(h1,i-mid,i);
int hval2=GetSubStrH(h2,sz-1-(i+mid),sz-1-i);
if(hval1!=hval2) r=mid;
else l=mid+1;
}
return 2*(l-1)+1;
}else{
int l=1,r=min(i+1,sz-i)+1;
while(l<r){
int mid=(l+r)/2;
int hval1=GetSubStrH(h1,i-mid+1,i);
int hval2=GetSubStrH(h2,sz-1-(i+mid),sz-1-(i+1));
if(hval1!=hval2) r=mid;
else l=mid+1;
}
return 2*(l-1);
}
}
int main(){
pow_p[0]=1;
for(int i=1;i<MAXN;++i) pow_p[i]=(pow_p[i-1]*P)%MOD;
getline(cin,s);
int sz=s.size();
h1[0]=s[0];
for(int i=1;i<sz;++i) h1[i]=(h1[i-1]*P+s[i])%MOD;
reverse(s.begin(),s.end());
h2[0]=s[0];
for(int i=1;i<sz;++i) h2[i]=(h2[i-1]*P+s[i])%MOD;
int ans=0;
for(int i=0;i<sz;++i){
int k=max(BS(i,false),BS(i,true));
ans=max(ans,k);
}
cout<<ans;
}