散列 hash

1.问题 A: 谁是你的潜在朋友

题目描述

 

    “臭味相投”——这是我们描述朋友时喜欢用的词汇。两个人是朋友通常意味着他们存在着许多共同的兴趣。然而作为一个宅男,你发现自己与他人相互了解的机会 并不太多。幸运的是,你意外得到了一份北大图书馆的图书借阅记录,于是你挑灯熬夜地编程,想从中发现潜在的朋友。
    首先你对借阅记录进行了一番整理,把N个读者依次编号为1,2,…,N,把M本书依次编号为1,2,…,M。同时,按照“臭味相投”的原则,和你喜欢读同一本书的人,就是你的潜在朋友。你现在的任务是从这份借阅记录中计算出每个人有几个潜在朋友。

输入

 

    每个案例第一行两个整数N,M,2 <= N ,M<= 200。接下来有N行,第i(i = 1,2,…,N)行每一行有一个数,表示读者i-1最喜欢的图书的编号P(1<=P<=M)

输出

 

    每个案例包括N行,每行一个数,第i行的数表示读者i有几个潜在朋友。如果i和任何人都没有共同喜欢的书,则输出“BeiJu”(即悲剧,^ ^)

样例输入

4 5
2
3
2
1

样例输出

1
BeiJu
1
BeiJu

分析:

本题是要统计输入数据相同的个数,因此采用hash是比较好的做法,只需要把统计的次数减去1,就是潜在朋友的个数。

代码:

#include<cstring>
const int max=1000;
int a[max]={0};
int main(){
	int n,m;
	while(scanf("%d%d",&n,&m) !=EOF){
		int b[n];
		for(int i=0;i<n;i++){
			scanf("%d",&b[i]);
			a[b[i]]++;
		}
		for(int i=0;i<n;i++){
			if(a[b[i]]==1) printf("BeiJu\n");
			else printf("%d\n",a[b[i]]-1);
		}
		memset(a, 0, sizeof(a));
	}
	return 0;
}

其中,a[b[i]]也是将数组a按照b的顺序输出,可以保证输出的顺序。

 

2.问题 B: 分组统计

题目描述

先输入一组数,然后输入其分组,按照分组统计出现次数并输出,参见样例。

输入

输入第一行表示样例数m,对于每个样例,第一行为数的个数n,接下来两行分别有n个数,第一行有n个数,第二行的n个数分别对应上一行每个数的分组,n不超过100。

输出

输出m行,格式参见样例,按从小到大排。

样例输入

1
7
3 2 3 8 8 2 3
1 2 3 2 1 3 1

样例输出

1={2=0,3=2,8=1}
2={2=1,3=0,8=1}
3={2=1,3=1,8=0}

分析:

这题也是要统计数字出现的次数,同时给出了不同的组,因此问题可以分解为,先统计属于同一组的数字,然后统计同一组中相同数字的个数,然后sort排序后,分组输出。

代码:

#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
const int MaxN = 102;
int main()
{
    //利用链表散列进行统计,这里用二维数组模拟
    int m, n;
    while (cin >> m)
    {
        while (m--)
        {
            int Table[MaxN][MaxN] = {0}, classFlag[MaxN] = {false}, Class[MaxN], ClaN = 0, Num[MaxN], tmp, NumUi[MaxN], N = 0;
            cin >> n;
            for (int i = 0; i < n; ++i)
                cin >> Num[i];//读入第一行所有数 
            for (int i = 0; i < n; ++i)
            {
                cin >> tmp;
                if (!classFlag[tmp])
                {
                    classFlag[tmp] = true;
                    Class[ClaN++] = tmp;//该组的序号第一次出现时,记录进来 
                }
                Table[tmp][Table[tmp][MaxN - 1]++] = Num[i];//建立这个表,第几组就是第几行 ,避免了数组越界 
            }//该数是第几组存进第几行的,存进哪一列呢?从第1列,开始存,该行最后一列记录了该组的数字个数 
            sort(Class, Class + ClaN);//组序号排序 ,ClaN记录组的个数 
            sort(Num, Num + n);//第一行数字排序 
            /*剔除重复项,建立新数组*/
            for (int i = 0; i < n; ++i)
            {
                if (N == 0 || Num[i] != NumUi[N-1])
                {
                    NumUi[N++] = Num[i];
                }
            }

            for (int k = 0; k < ClaN; ++k)
            {
                printf("%d={", Class[k]);/*组号*/
                for (int h = 0; h < N; ++h)/*按第一行数字顺序*/
                {   /*查询,如果出现相同的,则数字加1*/
                    int c = 0, j = 0;
                    for (; j < Table[Class[k]][MaxN - 1]; ++j)
                    {
                        if (Table[Class[k]][j] == NumUi[h])
                            ++c;
                    }
                    printf("%d=%d", NumUi[h], c);
                    if (h < N-1)
                        printf(",");
                }
                printf("}\n");
            }
        }
    }

    return 0;
}

这道题一时半会没有做出来,也是看的别人的代码,仿写的,本文只是为了记录自己的心得

代码思路

1.

for (int i = 0; i < n; ++i)
            {
                cin >> tmp;
                if (!classFlag[tmp])
                {
                    classFlag[tmp] = true;
                    Class[ClaN++] = tmp;//该组的序号第一次出现时,记录进来 
                }
                Table[tmp][Table[tmp][MaxN - 1]++] = Num[i];

此处的代码,是为了将属于同一组的数字放在数组table的一行中,便于下一步的处理,classflag数组为记录某一组代码是否已经存在,class记录出现了哪些组别,只有当classflag为false时,即二维数组中不存在该组数据时,将其记录到class中。而且,其中的table数组定义为102位,最后一位存放的是每行数字的个数

Table[tmp][MaxN - 1]++,其中Table[tmp][MaxN - 1]=0。因此将同一组的值,一次存放在table某一行的0-num位中。

2.

for (int i = 0; i < n; ++i)
            {
                if (N == 0 || Num[i] != NumUi[N-1])
                {
                    NumUi[N++] = Num[i];
                }
            }

这行代码是将num数组中输入的相同的数清理出,保证下一步输出时,不会有同样的数,也是为了保证计数的可靠性。

3.

for (int h = 0; h < N; ++h)/*按第一行数字顺序*/
                {   /*查询,如果出现相同的,则数字加1*/
                    int c = 0, j = 0;
                    for (; j < Table[Class[k]][MaxN - 1]; ++j)
                    {
                        if (Table[Class[k]][j] == NumUi[h])
                            ++c;
                    }

统计每一行中,属于数组numui中数字的个数,并计数,其中Table[Class[k]][MaxN - 1]与上文代码中

Table[tmp][Table[tmp][MaxN - 1]++]   如单纯的看Table[Class[k]][MaxN - 1],则其代表的就是数组table某一行的最后一个值,即为0,但是由于上文中存在Table[tmp][MaxN - 1]++,即没出现一个组别tmp,就将相应的Table[tmp][MaxN - 1] 加上1,这样就可以统计出每组数字的个数。

总结:

对于这类题,应该将其的结题步骤分为很多步,同时也要注意数组的越界等问题

 

3.问题 D: String Subtraction (20)

题目描述

Given two strings S1 and S2, S = S1 - S2 is defined to be the remaining string after taking all the characters in S2 from S1. Your task is simply to calculate S1 - S2for any given strings. However, it might not be that simple to do it fast.

输入

Each input file contains one test case. Each case consists of two lines which gives S1 and S2, respectively. The string lengths of both strings are no more than 104. It is guaranteed that all the characters are visible ASCII codes and white space, and a new line character signals the end of a string.

输出

For each test case, print S1 - S2 in one line.

样例输入

They are students.
aeiou

样例输出

Thy r stdnts.

分析:

题目的意思就是讲字符串中特定的字母全部取出,因为不是计算个数,所以不需要考虑将字符串转换为整数,即不需要使用一下的代码,否则还要将数字解码为字母。

int hashfun(char a[],int len){
	int id=0;
	for(int i=0;i<len;i++){
		if(a[i]>='A'&&a[i]<='Z'){
			id =id*52+(a[i]-'A');
		}
		else if(a[i]>='a'&&a[i]<='z'){
			id =id*52+(a[i]-'a')+26;
		}
	}
	return id;
}

代码如下:

#include<cstdio>
#include<cstring>
int hashtable[10000]={0};
int main(){
	char a[10000];
	while(gets(a)){
		int n=strlen(a);
		char b[10000];
		gets(b);
		int m=strlen(b);
		for(int i=0;i<m;i++){
			if(b[i]=='\n') break;
			hashtable[b[i]] =1;
		}
		for(int i=0;i<n;i++){
			if(hashtable[a[i]] ==0)
				printf("%c",a[i]);
		}
	}
	return 0;
}

代码思路:

定义hash数组,将需要去掉的字母输入进去,同时把相应的位置置为1,然后输出字符串的时候,只有当其对应的数组为的值为0时,才能输出,这样当if(hashtable[a[i]] ==0)成立的时候,就说明现在的字母是需要去掉的字母,不输出即可。

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值