BZOJ4166: 月宫的符卡序列 manacher

题意:给出一个字符串,定义每个回文子串的价值为所有出现位置的中点(偶数长度向下取整)异或和,求所有价值中最大的。每个点5组串,每个串长100W
本来这一行要感谢某人提供解法,由于当事人不愿意就删掉了orz
看了一下别人的码长和内存感觉我写的肯定不是正解了。。。反正能过
首先学过回文自动机的都知道一个串里本质不同的回文子串最多有n个
但是回文自动机是从回文串的尾端拓展节点的,fail指针连接的是一系列尾部相同的回文串,它们中点各不相同,没法最后BFS一遍统计答案。
既然要考虑中点相同,那么自然想到枚举中点的manacher(好像回文串也没别的算法了吧。。。),于是考虑怎么用manacher维护本质不同的串。
上面也说过回文自动机不可行是因为每个点的fail与其尾部对齐,那么我们考虑构建一种中点对齐的树,用hash来将子串对应到节点。考虑manacher中若已经确定了初始延展长度,那么这个长度以内的串以前一定出现过了,我们只需要随着暴力拓展来查找没出现过的串并且接到前一个下面就可以了。这样就可以按拓扑序反向统计答案了。
注意数据规模100W,hash模数在int范围内有极大可能被卡,所以我写了双取模

#include<cstdio>
#include<ext/pb_ds/assoc_container.hpp>
#define gm 1000005
typedef unsigned long long ll;
typedef __gnu_pbds::gp_hash_table<ll,int> map;
map node;
int tot;
int n,fa[gm],sum[gm];
char s[gm];
namespace index
{
    template<size_t mob>
    struct __hash
    {
        size_t pow[gm],hash[gm];
        void compile()
        {
            pow[0]=1,hash[0]=0;
            for(n=1;s[n];++n)
            {
                pow[n]=(pow[n-1]*131u)%mob;
                hash[n]=(hash[n-1]*131u+s[n])%mob;
            }
        }
        size_t substr(int l,int r)
        {
            return (hash[r]+mob-size_t((ll)hash[l-1]*pow[r-l+1]%mob))%mob;
        }
    };
    __hash<32785993u> h1;
    __hash<19980403u> h2;
    inline void compile()
    {
        h1.compile(),h2.compile();
    }
    inline ll substr(int l,int r)
    {
        return (ll(h1.substr(l,r))<<32)|(ll(h2.substr(l,r)));
    }
};
using index::substr;
int T;
inline int newnode(int fno)
{
    fa[++tot]=fno,sum[tot]=0;
    return tot;
}
int d[gm<<1];
inline int min(int a,int b){return a<b?a:b;}
void manacher()
{
    static char t[gm<<1];
    t[0]='$';t[1]='#';
    int len=1;
    for(int i=1;i<n;++i)
    {
        t[++len]=s[i];
        t[++len]='#';
    }
    t[len+1]=0;
    int maxn=0,id=0;
    for(int i=1;i<=len;++i)
    {
        d[i]=(maxn>i)?min(maxn-i,d[(id<<1)-i]):1;
        int last=(d[i]==1)?0:(node[substr((i-d[i]>>1)+1,i+d[i]-1>>1)]);
        while(t[i-d[i]]==t[i+d[i]])
        {
            ++d[i];
            int &now=node[substr((i-d[i]>>1)+1,i+d[i]-1>>1)];
            if(!now) now=newnode(last);
            last=now;
        }
        if(last) sum[last]^=(i>>1)-1;
        if(i+d[i]>maxn) maxn=i+d[i],id=i;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        node.clear();tot=0;
        scanf("%s",s+1);index::compile();
        manacher();
        int ans=0;
        for(int i=tot;i;--i)
        {
            if(sum[i]>ans) ans=sum[i];
            sum[fa[i]]^=sum[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值