数据结构PTA习题:进阶实验5-3.3 基于词频的文件相似度 (30分)

进阶实验5-3.3 基于词频的文件相似度 (30分)

实现一种简单原始的文件相似度计算,即以两文件的公共词汇占总词汇的比例来定义相似度。为简化问题,这里不考虑中文(因为分词太难了),只考虑长度不小于3、且不超过10的英文单词,长度超过10的只考虑前10个字母。

输入格式:
输入首先给出正整数N(≤100),为文件总数。随后按以下格式给出每个文件的内容:首先给出文件正文,最后在一行中只给出一个字符#,表示文件结束。在N个文件内容结束之后,给出查询总数M(≤10​4​​),随后M行,每行给出一对文件编号,其间以空格分隔。这里假设文件按给出的顺序从1到N编号。

输出格式:
针对每一条查询,在一行中输出两文件的相似度,即两文件的公共词汇量占两文件总词汇量的百分比,精确到小数点后1位。注意这里的一个“单词”只包括仅由英文字母组成的、长度不小于3、且不超过10的英文单词,长度超过10的只考虑前10个字母。单词间以任何非英文字母隔开。另外,大小写不同的同一单词被认为是相同的单词,例如“You”和“you”是同一个单词。

输入样例:

3
Aaa Bbb Ccc
#
Bbb Ccc Ddd
#
Aaa2 ccc Eee
is at Ddd@Fff
#
2
1 2
1 3

输出样例:

50.0%
33.3%

思路:
做这道题也尝试了很多很多遍。。。
我是创建一个散列表指针数组,数组内每个单元指向一个散列表。每一个散列表代表一个文件。读入输入的每个文件内的单词,分别插入各个散列表。
最后遍历单词数少的那个文件1,在另一个文件2中查找文件1内的所有单词,记录重复的单词数进行计算。

C语言实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FILENUMBERWORD 10000//估计的一个文件内的单词数
typedef char * ElementType;
struct word
{
	ElementType str;
	struct word * Next;
};
typedef struct word * Word;
struct hashtable  //一个散列表代表一个文件
{
	Word file;
	int tablesize;//散列表表长
	int num;//记录文件内单词总数
};
typedef struct hashtable * HashTable;
void Scan(HashTable H);
void Insert(HashTable H, ElementType s);
int Find(HashTable H, ElementType s);
int Hash(ElementType s, int tablesize);
int main()
{
	int N, M;
	scanf("%d\n", &N);
	int i, j;
	HashTable * H; //创建散列表指针数组,文件i对应下标i-1的散列表
	H = (HashTable *)malloc(N * sizeof(HashTable));
	for (i = 0; i < N; i++)
	{
		H[i] = (HashTable)malloc(sizeof(struct hashtable));
		H[i]->file = (Word)malloc(FILENUMBERWORD * sizeof(struct word));
		H[i]->tablesize = 101;
		H[i]->num = 0;//初始化每个文件内单词数=0
		for (j = 0; j < FILENUMBERWORD; j++)
		{
			(H[i]->file)[j].str = (ElementType)malloc(11 * sizeof(char));
			(H[i]->file)[j].str[0] = '\0';
			(H[i]->file)[j].Next = NULL;
		}
	}
	//逐个扫描读入文件
	for (i = 0; i < N; i++)
	{
		Scan(H[i]);
	}
	int f1, f2;
	int ret;
	scanf("%d\n", &M);
	Word w;
	int sumsame = 0;
	int t;
	for (i = 0; i < M; i++)
	{
		sumsame = 0;//记录两个文件共有的单词数
		scanf("%d %d", &f1, &f2);
		if (H[f1 - 1]->num > H[f2 - 1]->num) { t = f1; f1 = f2; f2 = t; }//选择2个文件中单词数少的那个文件进行遍历
		//遍历文件1,对文件1内的每个单词在文件2中查找,找到了即为重复出现单词,没有找到则未重复
		for (j = 0; j < H[f1 - 1]->tablesize; j++)
		{
			w = H[f1 - 1]->file[j].Next;
			while (w != NULL)
			{
				ret = Find(H[f2 - 1], w->str);
				if (ret == 1) { sumsame++; }
				w = w->Next;
			}
		}
		//
		//printf("%d %d\n", H[f1 - 1]->num, H[f2 - 1]->num);
		//
		printf("%.1f%%\n", 100.0*sumsame / (double)(H[f1 - 1]->num + H[f2 - 1]->num - sumsame));
	}
	return 0;
}
//扫描读入每个文件
void Scan(HashTable H)
{
	ElementType s, sw;
	s = (ElementType)malloc(5000 * sizeof(char));
	sw = (ElementType)malloc(5000 * sizeof(char));
	gets(s);
	while (strcmp(s, "#") != 0) //一行中只给出一个字符#表示文件结束
	{
		int i = 0, j = 0;
		int flag = 0;
		while (1)
		{
			if (s[i] >= 'A'&&s[i] <= 'Z' || s[i] >= 'a'&&s[i] <= 'z')
			{
				flag = 1;
				if (s[i] >= 'A'&&s[i] <= 'Z')//大写字母转化成小写字母
				{
					s[i] = s[i] - 'A' + 'a';
				}
				sw[j] = s[i];
				j++;
			}
			else
			{
				if (flag == 1)//读入英文字母后,读入非英文字母表示一个单词输入结束
				{
					flag = 0;
					sw[j] = '\0';
					if (strlen(sw) > 10) { sw[10] = '\0'; }//长度超过10只考虑前10个字母
					if (strlen(sw) >= 3)//一个单词字母长度不小于3
					{
						Insert(H, sw);//向散列表中插入单词
					}
					j = 0;//下一个单词从头开始记录
					if (s[i] == '\0') { break; }
				}
				else {  //考虑没有有效单词的情况,不加这个最后一个测试点通不过
					if (s[i] == '\0') { sw[j] = '\0'; break; }
				}
			}
			i++;
		}
		gets(s);
	}
}
//向散列表中插入单词
void Insert(HashTable H, ElementType s)
{
	int r;
	r = Find(H, s);
	int index = Hash(s, H->tablesize);
	if (r == 1);
	else
	{
		Word p, q;
		p = (Word)malloc(sizeof(struct word));
		p->str = (ElementType)malloc(12 * sizeof(char));
		strcpy(p->str, s);
		q = H->file[index].Next;
		p->Next = q;
		H->file[index].Next = p;
		H->num++;
	}
}
//在散列表中查找单词s,找到返回1,没找到返回0
int Find(HashTable H, ElementType s)
{
	int i;
	int index = Hash(s, H->tablesize);
	Word p;
	p = H->file[index].Next;
	while (p != NULL)
	{
		if (strcmp(p->str, s) == 0)
		{
			return 1;
		}
		p = p->Next;
	}
	return 0;
}
//散列函数计算
int Hash(ElementType s, int tablesize)
{
	unsigned int h = 0;
	int i = 0;
	while (s[i] != '\0')
	{
		h = h * 32 + s[i] - 'a';
		i++;
	}
	return h % tablesize;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值