Hash欢乐赛

Hash欢乐赛总结
今天终于把Hash欢乐赛所有的题目都A了,Hash真的是一个靠RP的东西,一些题目我只写了单关键字而且没有挂链,结果还是靠着RP A了,也只能说运气好吧,但Hash确实非常实用,能使不少难题用简单的Hash骗到不少分甚至AC。

Problem 1
串匹配【str1.pas/c/cpp】
Description
给1定一个字符串S1~Sn,给定一个匹配串 s1~sm,求有多少匹配子串。
Input 【str1.in】
第一行两个数字:n,m
第二行一个字符串S1~Sn
第三行一个字符串s1~sm
Output 【str1.out】
一个数字,表示有多少匹配子串
Sample Input
5 2
ababa
ba
Sample Output
2
Score:
n<=5000000,m<=50,m<n
保证所有字符均为小写英文字符

【分析】这是一道比较水的Hash,关键是选一个好的值Mod,用双关键字是问题不大,但单关键字也能靠着RP A掉,我比较懒,就写了个单关键字Hash,把字母转成26进制数,过程中MOd mo,只要O(N)的效率便可求出解。在Hash的变化中,可以用DDTT讲到的O(1)的方法,删掉第一个数,添加最后一个数。但删数和添加也要注意技巧,如果删的是最后一个,添加的是第一个,那么就需要除,然而Mod过特殊值,因此除会有误差,从而会导致WA,因此我们调整一下方法,删除第一个数,乘以26后加上最后一个数,这样就不会WA了。

【代码】
#include<cstdio>
#include<cstring>
#include<cstdlib>

#define mo 9797797

int i,j,l,n,m,o,p,a[mo],k,k1,o1;
char ch[5000001],ch1[51];

int main()
{
    freopen("str.in","r",stdin);
    freopen("str.out","w",stdout);
    scanf("%d%d",&n,&m);
    scanf("%s",ch);
    scanf("%s",ch1);
    k=1;
    o=0;
    o1=0;
    m--;n--;
    for(i=m;i>=0;i--)
    {
                     o=(o+(ch[i]-'a')*k)%mo;
                     o1=(o1+(ch1[i]-'a')*k)%mo;
                     if(i>0)k=k*26%mo;
    }
    a[o]++;
    for(i=m+1;i<=n;i++)
    {
                       o=(o-(ch[i-m-1]-'a')*k%mo+mo)%mo;
                       o=(o*26+(ch[i]-'a'))%mo;
                       a[o]++;
    }
    printf("%d\n",a[o1]);
    //system("pause");
}


Problem 2
基因【orzrzz.pas/c/cpp】
Description
由于RZZ 实在是太强了,引起了 YZH对RZZ 表示无限崇敬。
一天YZH再次被 RZZ 虐得体无完肤,于是开始抱怨自己的基因遗传不好,
想要和RZZ 对比基因的类似程度,如果非常相似,YZH会非常开心,如果差异
很大,YZH会变得非常郁闷,由于YZH 太忙了,没有时间去搞这种东西,于是
把这个问题留给了你。
基因匹配需要满足以下条件:它们的最长前缀的长度等于两者中较短者的长
度。
现在给定了N个RZZ 的基因和 M 个YZH 的基因,要求找出每一个 YZH基
因与多少个RZZ 基因相匹配。
Input 【orzrzz.in】
第一行两个数字:N,M
下面N行,每行开头一个数字len,接下来是一个长度为len 的RZZ 基因
下面M 行,每行开头一个数字len,接下来是一个长度为len 的 YZH 基因
Output 【orzrzz.out】
M 行,表示每一个YZH基因与多少个RZZ 基因相匹配。
Sample Input
4 5
3 0 1 0
1 1
3 1 0 0
3 1 1 0
1 0
1 1
2 0 1
5 0 1 0 0 1
2 1 1
Sample Output
1
3
1
1
2
数据范围与约定: 100%的数据,M<=50000,N<=50000,len<=10000,保证读入不超时

【分析】这其实是一道字母树的题目,但由于只有0,1两个数,因此可以往左往右建树。如果是0,就往左查找、建树,如果是1,就往右查找、建树。在建树的过程中,对于每个节点k,都将down[k]+1,因为如果在搜索一个字符串的时候,能走完,就说明下面的所有节点的字符串都能和该基因匹配,当然走过的路径中有字符串节点也能匹配;不能走完,说明不能走下去了, 就不能加下面的所有节点,这题就很简单了。

【代码】
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;

int i,j,k,m,n,o,p,down[800000],a[800000],lef[800000],righ[800000],f[800000],len,cnt,now;

int main()
{
    
    freopen("orzrzz.in","r",stdin);
    freopen("orzrzz.out","w",stdout);
    scanf("%d%d",&n,&m);
    memset(lef,0,sizeof(lef));
    memset(righ,0,sizeof(righ));
    cnt=1;
    for(i=1;i<=n;i++)
    {
                     scanf("%d",&len);
                     now=1;
                     down[now]++;
                     for(j=1;j<=len;j++)
                     {
                                        scanf("%d",&o);
                                        if(o==0){if(lef[now]==0)lef[now]=++cnt;now=lef[now];down[now]++;}
                                        if(o==1){if(righ[now]==0)righ[now]=++cnt;now=righ[now];down[now]++;}
                     }
                     f[now]++;
    }
    for(i=1;i<=m;i++)
    {
                     bool flag=false;
                     int ans=0;
                     scanf("%d",&len);
                     now=1;
                     for(j=1;j<=len;j++)
                     {
                                        scanf("%d",&o);
                                        if(!flag)ans+=f[now];
                                        if(o==0&&!(flag)){if(lef[now]==0)flag=true;now=lef[now];}
                                        if(o==1&&!(flag)){if(righ[now]==0)flag=true;now=righ[now];}
                     }
                     if(!flag)ans+=down[now];
                     printf("%d\n",ans);
    }
    //system("pause");
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值