计蒜客 ICPC南京站网络赛 Skr(Manacher + 字符串哈希)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82430166

大致题意:给你一个数字串,让你找出这个串所有的回文子串转化成为的数字的和。

用到回文子串,很自然而然想到用manacher算法。首先manacher算法求出每一个位置的最大回文串的半径,然后我们考虑把每一个回文串抠出来,首先判断它是否被计算过,如果没算过则直接加到结果中。这个过程直接用字符串哈希解决。那么问题就是,时间复杂度的问题了。

可以证明,一个字符串的不重复的回文子串的个数应该是在len的级别,因此我们可以就按照上面所说暴力的计算每一个的贡献。但是,在实际枚举过程中,我们有可能会枚举到已经出现过的串,所以我们考虑对于一个半径从大到小枚举。即当半径为R的时候,如果这个串已经被计算过,那么可以直接跳过这个位置,因为比它半径小的串肯定也被计算了。如此一来,总的时间复杂度大概是O(N)的。但是由于是字符串哈希,所以说常数会比较大,再加上双关键字哈希,哈希出来的结果考虑用pbds库的gp_hash_table 来存储,这个映射基本可以做到O(1)级别。或者说用手工链式哈希也行。具体见代码:

#include<bits/stdc++.h>
#define Mod 1000000007
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)

using namespace __gnu_pbds;
using namespace std;

const int N = 2e6 + 10;

gp_hash_table<LL,bool> mp;
int pp[N<<1],num[N],t[N];
char s[N<<1];

int Hs[N],hs[N],p[N],P[N];
const LL key1=111,key2=100007;
const int mod=100001651,MOD=100001623;

void init(int n)
{
    p[0]=P[0]=1;
    for(int i=1;i<n;i++)
    {
        P[i]=P[i-1]*key1%MOD;
        p[i]=p[i-1]*key2%mod;
    }

}

void ins(char *s)
{
    Hs[0]=hs[0]=s[0]-'0'+1;
    for(int i=1;s[i];i++)
    {
        Hs[i]=(Hs[i-1]*key1%MOD+s[i]-'0'+1)%MOD;
        hs[i]=(hs[i-1]*key2%mod+s[i]-'0'+1)%mod;
    }
}

LL Hash(int l,int r)
{
    int tmp1=Hs[r],tmp2=hs[r];
    if (!l) return (LL)tmp1<<31|tmp2;
    tmp1=(tmp1-Hs[l-1]*(LL)P[r-l+1]%MOD+MOD)%MOD;
    tmp2=(tmp2-hs[l-1]*(LL)p[r-l+1]%mod+mod)%mod;
    return (LL)tmp1<<31|tmp2;
}

void manacher()
{
    int len=strlen(s);
    for(int i=len;i>=0;--i)
        s[i+i+1]=s[i],s[i+i]='#';
    int id,mx=0;
    for(int i=1;i<len+len+1;++i)
    {
        if (mx>i) pp[i]=min(pp[2*id-i],mx-i); else pp[i]=1;
        while(s[i-pp[i]]==s[i+pp[i]]&&i-pp[i]>=0&&i+pp[i]<len+len+1)++pp[i];
        if(i+pp[i]>mx) id=i,mx=pp[i]+i;
    }
    for(int i=1;i<len+len+1;++i) pp[i]=pp[i]/2;
}

int main()
{
    init(N); t[0]=1;
    for(int i=1;i<N;i++)
        t[i]=t[i-1]*10LL%Mod;
    while(~scanf("%s",s))
    {
        ins(s);
        int len=strlen(s);
        num[0]=s[0]-'0';
        for(int i=1;i<len;i++)
            num[i]=num[i-1]*10LL%Mod+s[i]-'0';
        int ans=0;
        manacher(); mp.clear();
        for(int i=0;i<len;i++)
        {
            for(int R=pp[i<<1|1];R>=1;R--)
            {
                int l=i-R+1,r=i+R-1;
                LL hash=Hash(l,r);
                if (!mp[hash]) mp[hash]=1; else break;
                int tmp=l?(num[r]-num[l-1]*(LL)t[r-l+1]%Mod+Mod)%Mod:num[r];
                ans=(ans+tmp)%Mod;
            }
            for(int R=pp[i<<1];R>=1;R--)
            {
                int l=i-R,r=i+R-1;
                LL hash=Hash(l,r);
                if (!mp[hash]) mp[hash]=1; else break;
                int tmp=l?(num[r]-num[l-1]*(LL)t[r-l+1]%Mod+Mod)%Mod:num[r];
                ans=(ans+tmp)%Mod;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82430166
今日推荐