刚开始是听说了要用manacher采取写的,但是不难看出要使用manacher,因为题目要求的1个半回文串,其实就是两个回文中心的交集,你要找两个回文中心,让它们的回文半径可以互相覆盖即可。问题转化成了对于一个回文中心,它覆盖的左侧的所有回文中心中( 向右覆盖的范围超过i )这样的点的数量。这样就变成了求区间中大于x的数的个数,可以用主席树解决。
#include<iostream>
#include<cstring>
#include <string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+20;
const int maxm=2e7+20;
char s[maxn],s_new[maxn];
int p[maxn];
int init()
{
int len=(int)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 id=0,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];
}
}
return len;
}
int n,m,cnt;
int tree[maxn],ls[maxm],rs[maxm],sum[maxm];
void update(int k,int pre,int &now,int l,int r){
now=++cnt;
ls[now]=ls[pre];
rs[now]=rs[pre];
sum[now]=sum[pre]+1;
if(l==r){
return;
}
int m=(l+r)>>1;
if(k<=m) update(k,ls[pre],ls[now],l,m);
else update(k,rs[pre],rs[now],m+1,r);
}
int query(int a,int b,int l,int r,int L,int R){
if(L<=l&&r<=R){
return sum[b]-sum[a];
}
int ans=0;
int mid=(l+r)>>1;
if(mid>=L)
ans+=query(ls[a], ls[b], l, mid, L, R);
if(mid<R)
ans+=query(rs[a], rs[b], mid+1, r, L, R);
return ans;
}
int main()
{
int T;
cin>>T;
while(T--){
cnt=0;
tree[0]=ls[0]=rs[0]=sum[0]=0;
scanf("%s",s);
int len=manacher();
m=len/2;
ll ans=0;
for(int i=1;i<m;i++)
{
int L=i-p[i*2]/2+1;
if(L!=i)
update(L,tree[i-1],tree[i],1,m);
else
tree[i]=tree[i-1];
}
for(int i=1;i<m;i++){
int R=i+p[i*2]/2-1;
int t=query(tree[i], tree[R], 1, m, 1, i);
ans+=t;
}
printf("%lld\n",ans);
}
}
但是我的板子一直过不了,一直TLE,借用了以为其他人的主席树板子才能过,好迷啊。
借用模版:https://blog.csdn.net/know_heng/article/details/78590061
待更新:树状数组离线做法