计蒜客 2018南京网络赛 I Skr(马拉车+哈希)

题目:给一串由0..9组成的数字字符串,求所有不同回文串的权值和。比如说“1121”这个串中有“1”,“2”,“11”,“121”三种回文串,他们的权值分别是1,2,11,121。最终输出ans=135

思路:昨天比赛时读题给读错了样例也没看怎么来的。写成了所有的回文串权值和。。。码完代码发现,玛德样例都不对,再读了一遍题,真是智障啊。

今天看到有大佬讨论马拉车+哈希的做法,(本菜鸡学的马拉车都搁浅了一年多了),就重新看了看kungbin的模板,写了一下。了解马拉车原理会发现它对这个题本身就有个剪枝(i 这个字符串往外扩的初始长度Mp[i]直接就是剪掉了1..Mp[i]长度之后的啦)。但是仍然会有重复的(我map去重T了),这里再去重的办法就很巧妙了%%%,然后具体算权值时hash预处理,然后O(1)查询就好

先贴一个manacher的模板

///求最长回文子串
const int MAXN=110010;
char Ma[MAXN*2];
int Mp[MAXN*2];
void Manacher(char s[],int len)
{
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0;i<len;i++)
    {
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;

    int mx=0,id=0;
    for(int i=0;i<l;i++)
    {
        Mp[i]=mx>i?min(Mp[2*id−i],mx−i):1;
        while(Ma[i+Mp[i]]==Ma[i−Mp[i]])
            Mp[i]++;
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
}
/*
* abaaba
* i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
* Ma[i]: $ # a # b # a # a # b # a #
* Mp[i]: 1 1 2 1 4 1 2 7 2 1 4 1 2 1
*/
char s[MAXN];
int main()
{
    while(scanf("%s",s)==1)
    {
        int len=strlen(s);
        Manacher(s,len);
        int ans=0;
        for(int i=0;i<2*len+2;i++)
            ans=max(ans,Mp[i]−1);
        printf("%d\n",ans);
    }
    return 0;
}

这个题的代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=2000000+10;
const ll mod=1e9+7;
const ull bas=100007;
ull p[maxn<<1],has[maxn<<1];
ll pw[maxn<<1],sum[maxn<<1];

const int MOD=4000007;
int first[maxn<<1],nxt[maxn<<1],cnt=0;
ull val[maxn<<1];
bool exist(ull now)
{
       int u=now%MOD;
       for(int i=first[u];i;i=nxt[i])
           if(val[i]==now) return true;
       nxt[++cnt]=first[u];
       first[u]=cnt;
       val[cnt]=now;
       return false;
}
ull gethas(int l,int r)
{
    return has[r]-has[l-1]*p[r-l+1];
}
ll solve(int l,int r)
{
    ull tmp=gethas(l,r);
    if(exist(tmp)) return 0;
    ll ans=sum[r]-sum[l-1]*pw[(r-l+1+1)/2]%mod+mod;
    if(ans>mod)
        ans%=mod;
    return ans;
}
char Ma[maxn<<1];
int Mp[maxn<<1];
ll Manacher(char s[],int len)
{
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0;i<len;i++)
    {
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;

    pw[0]=p[0]=1;
    has[0]=sum[0]=0;
    for(int i=1;i<=l;i++)
    {
        p[i]=p[i-1]*bas;
        has[i]=has[i-1]*bas+Ma[i];
        pw[i]=pw[i-1]*10%mod;
        if(Ma[i]>='0'&&Ma[i]<='9')
            sum[i]=(sum[i-1]*10+Ma[i]-'0')%mod;
        else
            sum[i]=sum[i-1];
    }

    ll ans=0;
    int mx=0,id=0;
    for(int i=0;i<l;i++)
    {
        if(Ma[i]!='#')
            ans=(ans+solve(i,i))%mod;
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]])
        {
            if(Ma[i+Mp[i]]!='#')
                ans=(ans+solve(i-Mp[i],i+Mp[i]))%mod;
            Mp[i]++;
        }
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
    return ans;
}
char s[maxn];
int main()
{
    scanf("%s",s);
    int len=strlen(s);
    printf("%lld\n",Manacher(s,len));
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值