2018 ICPC 南京 M题 Mediocre String Problem ——Gym - 101981M

https://blog.csdn.net/KXL5180/article/details/90142634

http://fastvj.rainng.com/problem/Gym-101981M

http://codeforces.com/gym/101981/attachments

继上一篇文章关于南京M题的做法,这次我有学习了字符串hash有写了一个字符串hash的做法

做法:第一步还是和上次一样用马拉车算法预处理回文串,得到s的以左端点为起点的字符串的个数。

然后对s串哈希,然后在对,t串反转在哈希,最后枚举s串中和t串末尾相同的字符的位置,然后二分LCP长度,然后

ans+=length*sum[i];就可以了

思路和上一次的差不多,唯一不同的就是LCP的求法改变了

时间复杂度是O(nlogn),但不过常数比后缀数组小多了,如果hash的姿势够帅是不会超时的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
const ll p=13131;
const ll mod=1e9+7;
char s[N],t[N],s_new[N];
int d[N],pos[N];
int init()
{
    int len=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;
}
void Manacher()
{
    int len=init();
    int id,mx=0;
    for(int i=1;i<len;i++)
    {
        if(i<mx) pos[i]=min(pos[2*id-i],mx-i);
        else pos[i]=1;
        while(s_new[i-pos[i]]==s_new[i+pos[i]])
            pos[i]++;
        if(mx<i+pos[i]){
            id=i;mx=i+pos[i];
        }
    }
    for(int i=2;i<len;i++)
    {
        if(s_new[i]=='#'&&pos[i]==1) continue;
        int x=i/2-pos[i]/2+1;
        int y=x+i/2+pos[i]/2-!(i&1);
        d[x]++;
        d[y/2+1]--;
    }
}
ll hs1[N],hs2[N],pw[N];

ll geths(ll *hs,int l,int r)
{
    return (hs[r]-hs[l-1]*pw[r-l+1]%mod+mod)%mod;
}

int ok(int len,int p,int t_len)
{
    ll t1=geths(hs1,p-len+1,p);
    ll t2=geths(hs2,t_len-len+1,t_len);
    if(t1==t2) return 1;
    return 0;
}

int main()
{
    pw[0]=1;
    for(int i=1;i<1000003;i++){
        pw[i]=pw[i-1]*p%mod;
    }
    scanf("%s%s",s,t);
    Manacher();
    int len=strlen(s);
    for(int i=1;i<=len;i++) {
        d[i]+=d[i-1];
    }
    hs1[0]=hs2[0]=1;
    for(int i=0;i<len;i++){
        hs1[i+1]=(hs1[i]*p+s[i])%mod;
    }
    int t_len=strlen(t);
    reverse(t,t+t_len);
    for(int i=0;i<t_len;i++){
        hs2[i+1]=(hs2[i]*p+t[i])%mod;
    }
    ll ans=0;
    for(int i=0;i<len-1;i++){
        if(s[i]!=t[t_len-1]) continue;
        int l=1,r=min(i+1,t_len),mid,cnt=0;
        while(l<=r){
            mid=(l+r)>>1;
            if(ok(mid,i+1,t_len))
                cnt=mid,l=mid+1;
            else
                r=mid-1;
        }
        ans+=1LL*cnt*d[i+2];
    }
    printf("%lld\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值