Hash欢乐赛

来源:互联网 发布:linux运行java jar包 编辑:程序博客网 时间:2024/04/29 03:23
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 9797797int 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
原创粉丝点击