The Preliminary Contest for ICPC Asia Shanghai 2019 G. Substring —— 特别的hash

This way

题意:

给你一个母串,q个询问,每次给你一个子串,设定两个串S,T相等当且仅当
1.|S|=|T|
2.S[0]=T[0]&&S[len-1]=T[len-1]
3.S与T的组成集合相同。
问你母串中有多少个子串

题解:

其它都想到了啊,但是怎么哈希没有想到,一般的哈希是
h = h ∗ p + s [ i ] h=h*p+s[i] h=hp+s[i]
这种形式,它可以判断先后,但是这道题它不要求先后,然后我就查到了一个很神奇的做法:
h = h + p [ s [ i ] ] h=h+p[s[i]] h=h+p[s[i]]
由多个质数的幂次相加,其实哈希好像就是乱搞,让它能离散分布即可。。
那么这道题就变得非常简单了,对于长度相同的字符串,我们可以把它放在一堆,直接查一次这个长度,那么哈希的次数也就是 n \sqrt{n} n 级别。然后注意map不能一次次清空,这样会T,我们只记录在查询中有的值。

#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
const int N=1e5+5;
ll p[35];
struct node
{
    int id,len,sta,en;
    ll h;
    bool operator< (const node& a)const
    {
        return len<a.len;
    }
}q[N];
char s[N],ss[N];
unordered_map<ll,int>num[26][26];
int ans[N];
void init(int n,int l)
{
    ll h=0;
    for(int i=0;i<l;i++)
        h=h+p[s[i]-'a'+1];
    if(num[s[0]-'a'][s[l-1]-'a'].count(h))
        num[s[0]-'a'][s[l-1]-'a'][h]++;
    for(int i=l;i<n;i++)
    {
        h+=p[s[i]-'a'+1]-p[s[i-l]-'a'+1];
        if(num[s[i-l+1]-'a'][s[i]-'a'].count(h))
            num[s[i-l+1]-'a'][s[i]-'a'][h]++;
    }
}
int main()
{
    p[0]=1;
    for(int i=1;i<=26;i++)
        p[i]=p[i-1]*101ll;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s);
        int n,lens=strlen(s);
        for(int i=0;i<26;i++)
            for(int j=0;j<26;j++)
                num[i][j].clear();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ss);
            int len=strlen(ss);
            q[i].sta=ss[0]-'a',q[i].en=ss[len-1]-'a',q[i].id=i;
            ll h=0;
            for(int i=0;i<len;i++)
                h=h+p[ss[i]-'a'+1];
            q[i].len=len,q[i].h=h;
            num[q[i].sta][q[i].en][h]=1;
        }
        sort(q+1,q+1+n);
        for(int i=1;i<=n;i++)
        {
            if(q[i].len!=q[i-1].len)
                init(lens,q[i].len);
            ans[q[i].id]=num[q[i].sta][q[i].en][q[i].h];
        }
        for(int i=1;i<=n;i++)
            printf("%d\n",ans[i]-1);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值