一中 2.20 哈希专题 题解
来源:互联网 发布:微博淘宝企业版 编辑:程序博客网 时间:2024/05/17 01:53
PS:已经很长时间没有发题解了,说明我的做题之旅并不顺利。总算把昨天哈希专题的四道题做完了,来小结一下。
1.给1定一个字符串S1~Sn,给定一个匹配串 s1~sm,求有多少匹配子串。(N<=5000000,M<=50)
据说,这题暴力都能过,我还是练了一下哈希。
真的是裸的哈希,但是我因为我初学,连双关键字都不想编,还是试了很多取模的值。
(PS.我认为这个双关键字并不是:a1=now%p1;a2=now%p2,然后对hash[a1*p1+a2](其中p1,p2是取模的数)进行处理。这样说白了还是单数组,难以去重。真正有效的是开两个两维数组,对于每个hash[now],可能有不同的值来得出的,因此我们把每个值都记录一下。但是怎么判断他们是否是同一个值呢?就是在计算呢now时,如法制炮的计算一个now2,并在hash2的数组中巴每种值得now2记录一下,最后再判断。)
4.N(1<=N<=100000)头YZH,一共 K(1<=K<=30)种特色,每头YZH有多种特色, 用二进制01表示它的特色ID。 比如特色ID 为13(1101),则它有第1、3、4种特色。[i,j]段被称为balanced 当且仅当K 种特色在[i,j]内拥有次数相同。求最大的[i,j]段长度。
明显,转化为二进制后要前缀和优化,但是光枚举的话也是n^2的效率。我们不由得想到了二分!!白高兴一场,这不是单调递增的效果,比如4满足答案,但3可以不满足。
后来WWT大神来了,把此题轻松秒杀。
我们设K=3,且设某两段前缀和为:
......a1......a2......
......b1......b2......
......c1......c2......
首先,如果a2-a1=b2-b1=c2-c1,那么这两段可以更新答案。
我们转化一下a2-b2=a1-b1,c也是同理。
也就是说,如果每组数同时减去a1后,如果所剩的数相同,就是可行的,
于是哈希横空出世!听说人家单关键字卡了半天,卡出了一个取模数p=1593573.
但是我觉得这样不厚道,于是开了个刚才说的双关键字哈希。
AC代码:
题解完!
1.给1定一个字符串S1~Sn,给定一个匹配串 s1~sm,求有多少匹配子串。(N<=5000000,M<=50)
据说,这题暴力都能过,我还是练了一下哈希。
真的是裸的哈希,但是我因为我初学,连双关键字都不想编,还是试了很多取模的值。
(PS.我认为这个双关键字并不是:a1=now%p1;a2=now%p2,然后对hash[a1*p1+a2](其中p1,p2是取模的数)进行处理。这样说白了还是单数组,难以去重。真正有效的是开两个两维数组,对于每个hash[now],可能有不同的值来得出的,因此我们把每个值都记录一下。但是怎么判断他们是否是同一个值呢?就是在计算呢now时,如法制炮的计算一个now2,并在hash2的数组中巴每种值得now2记录一下,最后再判断。)
代码:
#include<stdio.h>using namespace std;const long mod=9797797;long hash[10000008];long i,m,n;char a[5000001],b[51];int main(){ freopen("str.in","r",stdin);freopen("str.out","w",stdout); scanf("%ld%ld\n",&n,&m);m--;n--; scanf("%s\n",a);scanf("%s",b); long chen=1;long now=0,now2=0; for (i=m;i>=0;i--) { long the=(chen*(a[i]-'a'))%mod; long the2=(chen*(b[i]-'a'))%mod; now=(now+the)%mod; now2=(now2+the2)%mod; if (i>0) chen=chen*26%mod; } hash[now]++; for (i=m+1;i<=n;i++) { now=(now+mod-(a[i-m-1]-'a')*chen%mod)%mod; now=now*26%mod; now=(now+a[i]-'a')%mod; hash[now]++; } printf("%ld",hash[now2]); return 0;}
2.现在给定了N个RZZ 的基因和 M 个YZH 的基因,要求找出每一个 YZH基因与多少个RZZ 基因相匹配。 基因匹配需要满足以下条件:它们的最长前缀的长度等于两者中较短者的长度。(N,M<=5万)
我们可以根据N个RZZ的基因构建一个字母树。(忘了说了,基因是由0和1组成的)同时,在每个基因的结尾标记一下。
构建完后,我们把M个YZH的基因一一带进去得解。
对于每个YZH的基因,分3种情况讨论:
(1)它到字母树的某处时突然没了,此时那个结点并不是叶子节点。
处理:ans=该节点祖先中所有结尾个数+该节点孩子中所有的结尾个数+该节点所有的结尾个数(一般是1,万一数据坑)
(2)它到字母树的某结点时,下一个字符是0(或1),而该节点的孩子只有1(或0)。
处理:ans=该节点祖先中所有结尾个数+该节点所有的结尾个数
(3) 它到字母树的某结点时,该节点已经没有孩子了。
处理:ans=该节点祖先中所有结尾个数+该节点所有的结尾个数
我按这个编了,结果超时了部分点。
超时代码:
后经过HHD大牛的改进,总算A了。因为在求每个点的孩子结点时,要遍历多次,对于每条基因,最坏要遍历n次!
我们在每个结点时开一个down数组,在构建字母树时预处理。
AC代码:
我们可以根据N个RZZ的基因构建一个字母树。(忘了说了,基因是由0和1组成的)同时,在每个基因的结尾标记一下。
构建完后,我们把M个YZH的基因一一带进去得解。
对于每个YZH的基因,分3种情况讨论:
(1)它到字母树的某处时突然没了,此时那个结点并不是叶子节点。
处理:ans=该节点祖先中所有结尾个数+该节点孩子中所有的结尾个数+该节点所有的结尾个数(一般是1,万一数据坑)
(2)它到字母树的某结点时,下一个字符是0(或1),而该节点的孩子只有1(或0)。
处理:ans=该节点祖先中所有结尾个数+该节点所有的结尾个数
(3) 它到字母树的某结点时,该节点已经没有孩子了。
处理:ans=该节点祖先中所有结尾个数+该节点所有的结尾个数
我按这个编了,结果超时了部分点。
超时代码:
#include<stdio.h>using namespace std;long left[8000001],right[8000001],f[8000001],cnt,n,m,i,j,t,k,x,ans;bool flag;void go(long k){ ans+=f[k]; if (left[k]!=0) go(left[k]); if (right[k]!=0) go(right[k]);}int main(){ freopen("orzrzz.in","r",stdin);freopen("orzrzz.out","w",stdout); scanf("%ld%ld",&n,&m);cnt=1; for (i=1;i<=n;i++) { scanf("%ld",&t);k=1; for (j=1;j<=t;j++) { scanf("%ld",&x); if (x==0){if (left[k]==0) left[k]=++cnt;k=left[k];} if (x==1){if (right[k]==0) right[k]=++cnt;k=right[k];} } f[k]++; } for (i=1;i<=m;i++) { scanf("%ld",&t);k=1;ans=0;flag=false; for (j=1;j<=t;j++) { scanf("%ld",&x); if (!flag) ans+=f[k]; if (x==0&&!flag){if (left[k]==0) flag=true;k=left[k];} if (x==1&&!flag){if (right[k]==0) flag=true;k=right[k];} } if (!flag) {go(k);} printf("%ld\n",ans); } return 0;}
后经过HHD大牛的改进,总算A了。因为在求每个点的孩子结点时,要遍历多次,对于每条基因,最坏要遍历n次!
我们在每个结点时开一个down数组,在构建字母树时预处理。
#include<stdio.h>using namespace std;long left[1000001],right[1000001],f[1000001],down[1000001],cnt,n,m,i,j,t,k,x,ans;bool flag;int main(){ freopen("orzrzz.in","r",stdin);freopen("orzrzz.out","w",stdout); scanf("%ld%ld",&n,&m);cnt=1;down[1]=0; for (i=1;i<=n;i++) { scanf("%ld",&t);k=1;down[1]++; for (j=1;j<=t;j++) { scanf("%ld",&x); if (x==0){if (left[k]==0) left[k]=++cnt;k=left[k];down[k]++;} if (x==1){if (right[k]==0) right[k]=++cnt;k=right[k];down[k]++;} } f[k]++; } for (i=1;i<=m;i++) { scanf("%ld",&t);k=1;ans=0;flag=false; for (j=1;j<=t;j++) { scanf("%ld",&x); if (!flag) ans+=f[k]; if (x==0&&!flag){if (left[k]==0) flag=true;k=left[k];} if (x==1&&!flag){if (right[k]==0) flag=true;k=right[k];} } if (!flag) ans+=down[k]; printf("%ld\n",ans); } return 0;}
3.经过众蒟蒻研究,DJ 在讲课之前会有一个长度为 N方案,我们可以把它看作一个数列;同样,花神在听课之前也会有一个嘲讽方案,有 M个,每次会在 x 到 y 的这段时间开始嘲讽,为了减少题目难度,每次嘲讽方案的长度是一定的,为K。花神嘲讽DJ 让DJ 尴尬需要的条件:在x~y 的时间内 DJ 没有讲到花神的嘲讽方案, 即J 的讲课方案中的x~y 没有花神的嘲讽方案【这样花神会嘲讽J 不会所以不讲】 。经过众蒟蒻努力,在一次讲课之前得到了花神嘲讽的各次方案,DJ 得知了这个消息以后欣喜不已,DJ 想知道花神的每次嘲讽是否会让DJ 尴尬【说不出话来】 。
对于每一个嘲讽做出一个回答会尴尬输出‘Yes’ ,否则输出‘No’(N,M<=10万,k<=20)
这道题开始时十分纠结。开哈希的话,这最多是10万进制;开字母树也存不下。
哈希挂链被卡代码几个点的代码:
(呵呵,常数太大了!!)
对于每一个嘲讽做出一个回答会尴尬输出‘Yes’ ,否则输出‘No’(N,M<=10万,k<=20)
这道题开始时十分纠结。开哈希的话,这最多是10万进制;开字母树也存不下。
哈希挂链被卡代码几个点的代码:
(呵呵,常数太大了!!)
#include<stdio.h>using namespace std;const long maxn=100000;const long size=200;long hash[maxn+1][size+1],cnt[maxn+1],a[21],n,m,k,x,i,j,y;bool ans;bool find(long now,long start){ if (now==k+1) return true; if (start+k-now>y) return false; bool flag=false;long i; for (i=1;i<=cnt[a[now]];i++) { if (hash[a[now]][i]==start+1) flag=find(now+1,hash[a[now]][i]); if (flag) return true; } return false;}int main(){ freopen("taunt.in","r",stdin);freopen("taunt.out","w",stdout); scanf("%ld%ld%ld",&n,&m,&k); for (i=1;i<=n;i++) { scanf("%ld",&x); hash[x][++cnt[x]]=i; } for (i=1;i<=m;i++) { scanf("%ld%ld",&x,&y); for (j=1;j<=k;j++) scanf("%ld",&a[j]); ans=false; if (y-x+1<k) ans=false; else { for (j=1;j<=cnt[a[1]];j++) if (hash[a[1]][j]>=x) { ans=find(2,hash[a[1]][j]); if (ans) break; } } if (ans) printf("No\n");else printf("Yes\n"); } return 0;}
后听说暴力有效O(∩_∩)O~~于是秒过。
AC代码:
AC代码:
#include<stdio.h>using namespace std;const long maxn=100000;const long size=200;long hash[maxn+1][size+1],cnt[maxn+1],a[21],n,m,k,x,i,j,y;bool ans;bool find(long now,long start){ if (now==k+1) return true; if (start+k-now>y) return false; bool flag=false;long i; for (i=1;i<=cnt[a[now]];i++) { if (hash[a[now]][i]==start+1) flag=find(now+1,hash[a[now]][i]); if (flag) return true; } return false;}int main(){ freopen("taunt.in","r",stdin);freopen("taunt.out","w",stdout); scanf("%ld%ld%ld",&n,&m,&k); for (i=1;i<=n;i++) { scanf("%ld",&x); hash[x][++cnt[x]]=i; } for (i=1;i<=m;i++) { scanf("%ld%ld",&x,&y); for (j=1;j<=k;j++) scanf("%ld",&a[j]); ans=false; if (y-x+1<k) ans=false; else { for (j=1;j<=cnt[a[1]];j++) if (hash[a[1]][j]>=x) { ans=find(2,hash[a[1]][j]); if (ans) break; } } if (ans) printf("No\n");else printf("Yes\n"); } return 0;}
4.N(1<=N<=100000)头YZH,一共 K(1<=K<=30)种特色,每头YZH有多种特色, 用二进制01表示它的特色ID。 比如特色ID 为13(1101),则它有第1、3、4种特色。[i,j]段被称为balanced 当且仅当K 种特色在[i,j]内拥有次数相同。求最大的[i,j]段长度。
明显,转化为二进制后要前缀和优化,但是光枚举的话也是n^2的效率。我们不由得想到了二分!!白高兴一场,这不是单调递增的效果,比如4满足答案,但3可以不满足。
后来WWT大神来了,把此题轻松秒杀。
我们设K=3,且设某两段前缀和为:
......a1......a2......
......b1......b2......
......c1......c2......
首先,如果a2-a1=b2-b1=c2-c1,那么这两段可以更新答案。
我们转化一下a2-b2=a1-b1,c也是同理。
也就是说,如果每组数同时减去a1后,如果所剩的数相同,就是可行的,
于是哈希横空出世!听说人家单关键字卡了半天,卡出了一个取模数p=1593573.
但是我觉得这样不厚道,于是开了个刚才说的双关键字哈希。
AC代码:
#include<stdio.h>const long l=4;const long l2=128;const long p=10017;const long q=1593573;long hash[p][201],hash2[p][201],cnt[p],a[31][100001];long n,k,i,j,now,two,chen,chen2,ans,x,kk;bool flag;using namespace std;int main(){ freopen("lineup.in","r",stdin);freopen("lineup.out","w",stdout); scanf("%ld%ld",&n,&k); for (i=1;i<=n;i++) { scanf("%ld",&x);kk=0; while (x>0) { kk++; a[kk][i]+=x&1; x=x>>1; } } for (i=1;i<=n;i++) { for (j=1;j<=k;j++) a[j][i]+=a[j][i-1]; } ans=0; for (i=0;i<=n;i++) { chen=1;now=two=0;chen2=1; for (j=1;j<=k;j++) { now=(now+(a[j][i]-a[k][i]+p)*chen)%p; two=(two+(a[j][i]-a[k][i]+q)*chen2)%q; chen=chen*l%p;chen2=chen2*l2%q; } flag=true; for (j=1;j<=cnt[now];j++) if (hash2[now][j]==two) { if (i-hash[now][j]>ans) ans=(i-hash[now][j]); flag=false; } if (flag) { hash[now][++cnt[now]]=i; hash2[now][cnt[now]]=two; } } printf("%ld",ans); return 0;}
题解完!
1 0
- 一中 2.20 哈希专题 题解
- 一中单调队列专题
- NOIP2016 模拟赛[一中题]题解&总结
- 重庆一中Vijos 【训练题】步步为零 P1428 题解
- DP专题练习题解
- 【题解】UESTC_DP专题
- scu图论专题题解
- 2017UESTC 数据结构专题题解
- 哈希专题总结
- 专题 简单搜索(bfs+dfs) 个人题解
- 2017UESTC 动态规划专题题解
- kuangbin专题一 简单搜索 题解思路
- 一中scanf
- uva188 完美哈希题解
- HDU 2041--超级楼梯题解(递推求解专题)
- 点分治专题——bzoj 1468 &bzoj 2152 题解
- splay专题复习——bzoj 3224 & 1862 & 1503 题解
- [kuangbin带你飞]专题九 连通图 题解报告
- uint8_t / uint16_t / uint32_t /uint64_t 的简单介绍
- asp.net输出js通用类jquery ui版
- Class<?>
- FZU 2103 Bin & Jing in wonderland 排列求概率
- 借助NDK,Android与C动态链接库的相互调用
- 一中 2.20 哈希专题 题解
- zip命令
- linux 技巧 免除密码
- 实际开发过程
- (void) (&_x == &_y)
- Beautiful Soup库入门并用其构建简单的网页爬虫
- jQuery AJAX —篇四 $.getJSON()
- 最大流和费用流问题中使用反向弧的原因
- C# 中使用OPenCV(Emgu)心得