bzoj4166 月宫的符卡序列

本文介绍了一种使用Manacher算法和Hash技术解决特定字符串问题的方法,即寻找所有回文子串中最大价值的问题。通过构建Hash表来存储不同回文子串及其对应的中点异或值,最终找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定一个包含26个小写字母的字符串,字符从0开始标号。一个子串a的价值定义为:a在S中的所有出现位置中点的异或值。(规定:若a出现在s[l……r],那么a的中点为 l+r2() ).求:对于所有 回文子串 a,最大的价值是多少?

输入格式

第一行,一个整数num,( 1num5 ),表示数据组数;
每组数据占一行,有一个仅含小写字母的字符串S.

输出格式

对于每组数据,输出一行,表示最大价值.

样例

样例输入

1
aabacabaaa

样例输出

15

样例说明

对于回文子串aa,所有出现位置的中点为:0,7,8;
0^7^8=15

题解

回文自动机记的是右端点,似乎不太好用,关键是我不会写回文树。
所以还是考虑一下manacher+hash.
先用manacher跑一遍字符串,可以看出,只有在暴力扩展右边界时会产生新的本质不同的回文串.
这些本质不同的回文串的数目是O(n)的。
我们需要把中间部分相同的串连接起来形成一棵树.
比如:abbba是bbb的子节点,bbb是b的子节点.子串用hash值映射到节点.
本来用的是map,发现超时很严重,于是看了下网上各位大佬的代码,发现大佬们用的是hash_table.
居然用黑科技!我等蒟蒻还是用链表hash算了。

代码(很丑)

#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<map> 
using namespace std; 
#define MAXN 1005000 
#define LL long long 
#define ULL unsigned long long 
const ULL s1=131,s2=129; 
const ULL m1=19980403,m2=998244353; 
char s[MAXN],t[MAXN*2]; 
ULL h1[MAXN],h2[MAXN],pow1[MAXN],pow2[MAXN];
int Next[MAXN<<2],End[MAXN<<2],Last[m1+10],Len[MAXN<<2],e=0;
void add(ULL x,int p)
{
    ULL a,b;
    a=x>>32;
    b=x-(a<<32);
    End[++e]=b;
    Len[e]=p;
    Next[e]=Last[a];
    Last[a]=e;
    return ;
}
int find(ULL x)
{
    ULL a,b;
    a=x>>32;
    b=x-(a<<32);
    for(int t=Last[a];t;t=Next[t])
    {
        if(End[t]==b)
        {
            return Len[t];
        }
    }
    return -1;
}
int L,len; 
map<ULL,int>T; 
void init() 
{ 
    pow1[0]=pow2[0]=1; 
    h1[0]=h2[0]=0; 
    for(int i=1;i<=L;i++) 
    { 
        pow1[i]=pow1[i-1]*s1%m1; 
        pow2[i]=pow2[i-1]*s2%m2; 
        h1[i]=(h1[i-1]*s1+s[i])%m1; 
        h2[i]=(h2[i-1]*s2+s[i])%m2; 
    } 
    return ; 
} 
ULL cal(int l,int r) 
{ 
    ULL t1,t2; 
    t1=(h1[r]+m1-h1[l-1]*pow1[r-l+1]%m1)%m1; 
    t2=(h2[r]+m2-h2[l-1]*pow2[r-l+1]%m2)%m2; 
    return (t1<<32)|t2; 
} 
int fa[MAXN],dp[MAXN],p[MAXN*2],tot=0,ans=0; 
void work() 
{ 
    memset(p,0,sizeof(p));
    memset(s,0,sizeof(s));
    memset(Last,0,sizeof(Last));
    e=0;
    //T.clear(); 
    tot=0; 
    scanf("%s",s+1); 
    L=strlen(s+1); 
    init(); 
    t[0]='$'; 
    len=0; 
    for(int i=1;i<=L;i++) 
    { 
        t[++len]='#'; 
        t[++len]=s[i]; 
    } 
    t[++len]='#'; 
    t[++len]='@'; 
    int mx=0,id=0,lst; 
    ULL tt;
    for(int i=1;i<len;i++) 
    { 
        p[i]=mx>i?(min(mx-i,p[(id<<1)-i])):1; 
        //lst=(p[i]==1)?0:T[cal(((i-p[i])>>1)+1,(i+p[i]-1)>>1)];
        lst=(p[i]==1)?0:find(cal(((i-p[i])>>1)+1,(i+p[i]-1)>>1)); 
        for(;t[i-p[i]]==t[i+p[i]];) 
        { 
            ++p[i]; 
            tt=cal(((i-p[i])>>1)+1,(i+p[i]-1)>>1);
            if(find(tt)<0) 
            { 
                //T[tt]=++tot;
                ++tot;
                add(tt,tot); 
                dp[tot]=0;
                fa[tot]=lst; 
                lst=tot; 
            } 
            else 
            { 
                //lst=T[tt]; 
                lst=find(tt);
            } 
        } 
        if(lst) 
        { 
            dp[lst]^=(i>>1)-1; 
        } 
        if(i+p[i]>mx) 
        { 
            mx=i+p[i]; 
            id=i; 
        } 
    } 
    ans=0; 
    for(int i=tot;i>=1;i--) 
    { 
        ans=max(ans,dp[i]); 
        dp[fa[i]]^=dp[i]; 
    } 
    printf("%d\n",ans); 
    return ; 
} 
int main() 
{ 
    int Tt; 
    scanf("%d",&Tt); 
    while(Tt) 
    { 
        work(); 
        --Tt; 
    } 
    return 0; 
}

上面这段丑代码,显然是用了map发现T了,然后改成链表的。链表部分的变量名沿用了链式存图的变量名.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值