首页 > 代码库 > csu 2014 summer trainning day 1 哈希
csu 2014 summer trainning day 1 哈希
POJ 1200
题意:给定串s,串中不同字符数nc,所求子串长度n,求长度为n的不同的子串的个数
分析:处理长度很短的字符串哈希,数据保证可以无冲突存储下来,利用hash思想快速查询以前便利的结果,关键在于优化搜索。
code:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <set> 5 using namespace std; 6 7 int c[270]; 8 bool Hash[13000000]; 9 char s[1000100];10 int d,l;11 void init(int len){12 memset(c,0,sizeof(c));13 memset(Hash,0,sizeof(Hash));14 int cnt=0;15 for(int i=0;i<len;i++){16 if (c[s[i]]==0){17 c[s[i]]=++cnt;18 }19 if (cnt==d) break;20 }21 return ;22 }23 void solve(int len){24 int ans=0;25 int v=0;26 int p=1;27 for(int i=1;i<l;i++) p*=d;28 for(int i=0;i<l;i++){29 v=v*d+c[s[i]];30 }31 Hash[v]=true;32 ans++;33 for(int i=l;i<len;i++){34 v=(v-c[s[i-l]]*p)*d+c[s[i]];35 if (!Hash[v]){36 ans++;37 Hash[v]=true;38 }39 }40 printf("%d\n",ans);41 return ;42 }43 int main(){44 while(~scanf("%d%d",&l,&d)){45 scanf("%s",s);46 int len=strlen(s);47 init(len);48 solve(len);49 }50 return 0;51 }
POJ 1635
题意:给定一棵有根树的最小表示,即便利的结果,远离树根表示0,靠近树根表示1,给定两串不同便得到的01序列,判断两棵树是否是同构的。
分析:1、判断树同构的方法,统计F(x),表示子节点数为x的节点的个数,若两个树的F(x)在定义域内是完全相等的,则两棵树同构。
2、分析01串:在纸上举几个例子,就能得到规律。便利的结果是满足栈的结构的,所以处理出每个节点在串中出现(一个0,一个1)的两个位置,
那么这两个位置对应的节点的子节点的个数就是,字符串中这两个位置夹的0(或1)的个数。预处理O(n),统计O(n)
code:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <set> 5 #include <stack> 6 using namespace std; 7 8 int t; 9 char s1[3010],s2[3010];10 int px1[3010],py1[3010];11 int px2[3010],py2[3010];12 int num1[3010],num2[3010];13 int cnt1,cnt2,len;14 int S[3010],p;//手动开的栈15 bool check(){16 memset(num1,0,sizeof(num1));17 memset(num2,0,sizeof(num2));18 for(int i=1;i<=cnt1;i++){19 int x=px1[i];20 int y=py1[i];21 int c=0;22 for(int i=x+1;i<y;i++){23 if (s1[i]==‘0‘) c++;24 }25 num1[c]++;26 }27 for(int i=1;i<=cnt2;i++){28 int x=px2[i];29 int y=py2[i];30 int c=0;31 for(int i=x+1;i<y;i++){32 if (s2[i]==‘0‘) c++;33 }34 num2[c]++;35 }36 for(int i=0;i<=cnt2;i++){37 if (num1[i]!=num2[i]) return false;38 }39 return true;40 }41 void solve(){42 len=strlen(s1);43 if (len!=(int)strlen(s2)){44 puts("different");45 return ;46 }47 cnt1=0;p=0;48 for(int i=0;i<len;i++){49 if(s1[i]==‘0‘){50 S[++p]=++cnt1;51 px1[cnt1]=i;52 }53 if (s1[i]==‘1‘){54 py1[S[p--]]=i;55 }56 }57 //// for(int i=1;i<=cnt1;i++){58 //// cout<<px1[i]<<" "<<py1[i]<<endl;59 //// }60 cnt2=0;p=0;61 for(int i=0;i<len;i++){62 if(s2[i]==‘0‘){63 S[++p]=++cnt2;64 px2[cnt2]=i;65 }66 if (s2[i]==‘1‘){67 py2[S[p--]]=i;68 }69 }70 if (cnt1!=cnt2) {71 puts("different");72 return ;73 }74 if (check()){75 puts("same");76 }else puts("different");77 78 return ;79 }80 int main(){81 scanf("%d",&t);82 while(t--){83 scanf("%s",s1);84 scanf("%s",s2);85 solve();86 }87 return 0;88 }
POJ 1971
题意:给定1000个二维坐标节点,判断这些节点能构成的平行四边形有多少个。坐标范围<=1000000000
分析:数学知识是必要的,不然很难想到很快的搜索办法(反正开始时我没有),即平行四边形的两对对顶点的的中点是重合的。
那么,若存在两个重合的中点,那么这两条边交叉起来能构成一个平行四边形。
那么我们O(n^2)枚举任意两个点,处理出他们的中点(处理成整数,不除2)。
得到n^2/2个中点的我们现在要能快速找找到对于每种中点有几个是相等的。这道题不使用hash,排序后,成段扫描处理即可,当然,Hash更快。
1 #include <iostream> 2 #include <queue> 3 #include <string.h> 4 #include <stdio.h> 5 #include <algorithm> 6 using namespace std; 7 8 int t,n; 9 int x[1100],y[1100];10 struct N{11 int x,y;12 bool operator <(const N&X)const{13 if (x==X.x) return y<X.y;14 else return x<X.x;15 }16 bool operator==(const N&X)const{17 return x==X.x&&y==X.y;18 }19 20 }Ps[1100000];21 void print(int cnt){22 for(int i=0;i<cnt;i++){23 cout<<"("<<Ps[i].x<<","<<Ps[i].y<<")"<<endl;24 }25 }26 int cnt;27 int main(){28 scanf("%d",&t);29 while(t--){30 scanf("%d",&n);31 for(int i=0;i<n;i++){32 scanf("%d%d",&x[i],&y[i]);33 }34 cnt=0;35 for(int i=0;i<n-1;i++){36 for(int j=i+1;j<n;j++){37 Ps[cnt++]=(N){x[i]+x[j],y[i]+y[j]};38 }39 }40 sort(Ps,Ps+cnt);41 long long ans=0;42 int k=1;43 for(int i=1;i<cnt;i++){44 if (Ps[i]==Ps[i-1]){45 k++;46 }else{47 ans=ans+(long long)k*(k-1)/2;48 k=1;49 }50 }51 printf("%I64d\n",ans);52 }53 return 0;54 }
POJ 2002
题意:给定1000个二维坐标节点,判断它们能构成多少个正方形。
分析:1、不造为什么用上一题类似的方法会错???当然,要满足两个垂直一个重合
2、首先所有点(X^2+Y^2)%Mod哈希存一遍code
3、用到数学知识:
Point A=(Point){x[i]-y[i]+y[j],y[i]+x[i]-x[j]};Point B=(Point){x[j]-y[i]+y[j],y[j]+x[i]-x[j]};Point C=(Point){x[i]+y[i]-y[j],y[i]-x[i]+x[j]};Point D=(Point){x[j]+y[i]-y[j],y[j]-x[i]+x[j]};
枚举两点,公式求出另外两点,Hash查找这些点是否在图上,统计出个数,用组合数学即可处理
code:
1 #include <iostream> 2 #include <queue> 3 #include <string.h> 4 #include <stdio.h> 5 #include <algorithm> 6 #define Mod 49999 7 using namespace std; 8 struct Point{ 9 int x,y;10 int k;11 bool operator==(const Point&X){12 return x==X.x&&y==X.y;13 }14 };15 vector<Point>G[Mod+100];16 17 void init(){18 for(int i=0;i<Mod+10;i++) G[i].clear();19 }20 21 int Hash(Point P){22 return (P.x*P.x+P.y*P.y)%Mod;23 }24 25 int findx(Point P){26 int ha=Hash(P);27 int i;28 for(i=0;i<G[ha].size();i++){29 if (G[ha][i]==P) break;30 }31 if (i==G[ha].size()) return 0;32 return G[ha][i].k;33 }34 void Insert(Point P){35 int ha=Hash(P);36 int i;37 for(i=0;i<G[ha].size();i++){38 if (G[ha][i]==P) {39 G[ha][i].k++;40 break;41 }42 }43 if (i==G[ha].size()){44 G[ha].push_back((Point){P.x,P.y,1});45 }46 return ;47 }48 49 int n;50 int x[1100],y[1100];51 int main(){52 while(~scanf("%d",&n)&&n){53 init();54 for(int i=0;i<n;i++){55 scanf("%d%d",&x[i],&y[i]);56 Insert((Point){x[i],y[i],0});57 }58 int ans=0;59 for(int i=0;i<n-1;i++){60 for(int j=i+1;j<n;j++){61 Point A=(Point){x[i]-y[i]+y[j],y[i]+x[i]-x[j]};62 Point B=(Point){x[j]-y[i]+y[j],y[j]+x[i]-x[j]};63 Point C=(Point){x[i]+y[i]-y[j],y[i]-x[i]+x[j]};64 Point D=(Point){x[j]+y[i]-y[j],y[j]-x[i]+x[j]};65 int k1=findx(C);66 int k2=findx(D);67 int k3=findx(A);68 int k4=findx(B);69 ans+=(k1*k2)+(k3*k4);70 }71 }72 ans=ans/4;73 printf("%d\n",ans);74 }75 return 0;76 }
POJ 2549
题意:给定一集合S,给定集合中的所有数,统计A+B+C=D的个数,A、B、C、D皆取自S,且是各不相同的元素。集合的元素的个数<=1000
分析: 类似白书上的中途相遇法,等式可以这样看,A+B=D-C,两两枚举,Hash存储,再两两枚举D,A,统计即可。注意存的时候,要保留A、B,
因为A、B、C、D要不相同,所以要判断。
code:
1 #include <iostream> 2 #include <queue> 3 #include <string.h> 4 #include <stdio.h> 5 #include <algorithm> 6 #include <vector> 7 #define Mod 49999 8 9 using namespace std;10 struct Node{11 int add,a1,a2;12 };13 vector<Node>G[Mod+100];14 void init(){15 for(int i=0;i<Mod+10;i++) G[i].clear();16 }17 void Insert(int k,int b1,int b2){18 int key=k;19 while(key<0) key=key+Mod;20 key=key%Mod;21 int i;22 for(i=0;i<G[key].size();i++){23 int add=G[key][i].add;24 int a1=G[key][i].a1;25 int a2=G[key][i].a2;26 if (add == k && (a1+a2==b1+b2) && (a1-a2==b1-b2)) break;27 }28 if (i==G[key].size()) G[key].push_back(Node{k,b1,b2});29 return ;30 }31 bool Search(int d,int c){32 int key=d-c;33 while(key<0) key=key+Mod;34 key=key%Mod;35 int i;36 for(i=0;i<G[key].size();i++){37 int add=G[key][i].add;38 int a1=G[key][i].a1;39 int a2=G[key][i].a2;40 if (add==d-c && a1!=d && a2!=d && a1!=c && a2!=c) return true;41 }42 return false;43 }44 int a[1010];45 int n;46 int main(){47 while(~scanf("%d",&n)&&n){48 init();49 for(int i=0;i<n;i++) scanf("%d",&a[i]);50 for(int i=0;i<n-1;i++){51 for(int j=i+1;j<n;j++){52 Insert(a[i]+a[j],a[i],a[j]);53 }54 }55 int m=-536870913;56 for(int i=0;i<n-1;i++){57 for(int j=i+1;j<n;j++){58 if (Search(a[i],a[j])) m=max(m,a[i]);59 if (Search(a[j],a[i])) m=max(m,a[j]);60 }61 }62 if (m==-536870913) puts("no solution");63 else printf("%d\n",m);64 }65 return 0;66 }
列出一些Hash要用到的素数:
prime=997, prime=1999 , prime=7993 , prime=9973, prime=29989, prime=49999,prime=99991