本文写的内容可能在很多巨佬看来属实是一些简单的废话,但我的底子比较薄,很多东西都是想了好久,这篇文章的主要目的实际上也只不过是把我的一些改进的地方记录下来,防止以后忘记。。。

题目描述:

百度、谷歌等互联网搜索引擎提供高效的网页、文档搜索功能,用户可以通过一个和多个关键词查询感兴趣的网页信息。要实现超大规模的文本文档搜索,通常需要借助高效的索引和查询算法。编程实现一个基于关键词的文档搜索程序,实现对大规模文本文档的快速搜索和排序。具体方法如下:

1、对给定的文档(网页)集合(含N个文档)中每个文档进行单词(英文)提取,并统计每个单词k在每个文档d出现的频次(即出现次数)TNkd(该文档总词数为

),由此可以计算其词频TFkd

为了提高算法的准确性,在此只统计字典中出现且不为停用词(stop-word)的单词。单词为仅由字母组成的字符序列,包含大写字母的单词应将大写字母转换为小写字母后进行词频统计。

在课程网站下载区提供了字典“dictionary.txt”文件和英文停用词表“stopwords.txt”文件(文件中只包含单词,不含其解释,且已按字典序排序)。

说明:在自然语言处理中,停用词(stop-word)指的是文本分析时不会提供额外语义信息的词的列表,如英文单词a,an,he,you等就是停用词。

2、统计每个单词k在文档集合中出现的次数(DNk,即出现该单词的文档数),并计算其逆文档频率IDFk(log以10为底)。定义如下:

3、针对输入的关键词K1,K2,..,Km,按照TF-IDF对文档集合中的文档进行相关度打分。对任意一个文档d,针对所输入的关键词,其相关度计算公式如下:

若某个关键词未在文档集合中出现,则不用计算其IDFk,其对所有文档的相关度都为0。

4、依据相关度给出检索结果按由高至低进行排序,返回Top-N的结果。

为了简化搜索引擎的实现,从互联网上爬取(Web Crawling)相关网页(文档)的工作已经完成,并将爬取的网页文档数据已存入一个文本文件(aritcle.txt)中,其中每个网页第一行为网页标识号(如XX-XXXX,可按字符串来输入),然后为网页内容,网页文档间以换页符\f分隔。在课程网站下载区提供了一个用于测试的article.txt文件。

【输入形式】

从命令行输入作为需要返回的检索结果数量NUM和作为检索的关键词串K1,K2,..,Km

具体形式如下:

search NUM K1 K2 .. Km

其中search为搜索引擎运行程序,NUM与关键词之间以一个空格分隔。根据当前目录下的“dictionary.txt”文件、停用词文件“stopwords.txt”以及网页数据文件“article.txt”,按上面要求对网页文档进行相关度计算和排序。

注意:

1.输入串K1 K2 .. Km中的停用词及非字典中单词将不进行相关度分析。

2.由于Windows系统下文本文件中的’\n’回车符在(评测环境)Linux系统下会变为’\r’和’\n’2个字符,建议用fscanf(fp,”%s”,…)来处理字典文件和停用词文件中英文单词。

【输出形式】

先将Sim值排名前5(TOP 5)的网页信息输出到屏幕上,输出时先输出相关度Sim值(小数点后保留六位)、相应网页序号(从article.txt文件中读入网页文档时按序从1开始编号)及在文件article.txt中的标识号,三者之间由一个空格分隔,最后有一个回车。

同时将Sim值排名前NUM(TOP N)的网页信息输出到results.txt文件中,输出时先输出相关度Sim值(小数点后保留六位)、相应网页序号(从article.txt文件中读入网页文档时按序从1开始编号)及在文件article.txt中的标识号,三者之间由一个空格分隔,每个网页信息后有一个回车;若找到的网页文档数(即Sim值大于0的文档数,即包含所给关键词的文档数)少于NUM,则按实际数目输出(屏幕输出也如此)。

注意:如果相关度Sim值相同,则按照网页序号由小到大的顺序输出!

【样例输入】

假设search.exe为搜索引擎程序,以下面方式运行该程序:

search 100 edu news article

(运行程序前,从课程网站下载区下载文件:article.txt, dictionary.txt, stopwords.txt, results(样例).txt到本地)

说明:若本地编程环境为dev-C++,可点击菜单Execute\Parameters…,在下面对话框中输入相应命令行参数。

【样例输出】

程序运行后,屏幕上输出Top-5的结果为:

0.581744 24 1-24

0.466224 230 1-230

0.447891 543 1-543

0.446951 54 1-54

0.440138 87 1-87

所生成的结果文件“results.txt”内容应与下载区文件“results(样例).txt”完全相同。

【样例说明】

样例屏幕输出为按相关度排序由高到低排名前5的结果。其中每一行第一部分为网页文档的相关度(Sim)值,第二部分为相应文档在文件中的序号,第三部分为文档在文件中的标识号。文件results.txt中为按相关度排序由高到低排名前100的结果。

【评分标准】

本综合功能测试题,其评分标准为通过测试数据即可得满分。程序运行无结果或结果错误将不得分。


思路分析

最开始的时候没有多思考,因为对于一个.c文件到底能开多大的数组不太确定(),索性放弃了思考,按照最简单的想法,开一个巨大的数组将dictionary中所有词存入数组,设置这样一个结构体,flag标记是否为stopword,之后对应的DNk,DNkd。。。各种量全部储存下来(好家伙,做完一看真是什么牛马都存下来了),结果费了半天劲,终于将代码写完,结果一编译,编译器直接报错为

这还是我第一次见这种错误,急忙上了百度搜了搜,不出所料是数组开大了,之后就卡住了,原有的思路根本没法应对这个,之后再群里讨教大佬,大佬为我仙人指路,告诉我Trie树是一条名路,从此我便踏上了Trie树的道路。

最开始对于Trie树的印象是完全由链式结构组成,结果上网一看大佬的操作才知道数组也能玩的这么花,但是当时总感觉时间紧任务重,没有时间来理解这个方法了,于是直接照着网上的模板copy了插入与查找操作,最开始不理解,用的都没自信,写了一段时间便没勇气写下去了。

但ddl是第一生产力,不得已又开始去写,后来逐渐意识到了统计的单词只需要关键词的数据,完全没必要将所有单词存下来(实际上是舍友问我的时候我才看到的,属实是审题失误了),之后我将Words的组成缩减了,但是后来审视自己的代码时又发现完全没必要将Trie树变成结构体,这样[][26]中将会有大部分的空间是被浪费的,这时才反应过来可以变成[][28],前0~25号表示下一级的位置,26号表示关键词的位置,27号表示这是要统计的关键词,之后的操作就丝滑起来了。

可是,本地终于过了,交上去发现还是不对,我TM改了若干处才发现是命令行参数用错了,这才修成正果(泪目了,蒟蒻的眼泪从嘴角滑了出来)


代码部分:(请求巨佬批评指正)

// pro1:Trie树确定索引位置; 用stopWord除去不该计算的词
// pro2:以文档为单位(N)记录每个单词的频次;
//##data:以结构体{TNkd,DNk,TFkd[pages],IDFk}来记录##ki的TNkd++,DNk++
// pro3:一篇文档结束后,计算TFkd,TNkd清0,读入文档时记录对应的文档数以及其网页序号;
// pro4:读完所有文档,
// pro5:计算IDFk,计算完计算Sim
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#define PageSize 16000
#define WordSize 10000
struct node {int TNkd, DNk;double TFkd[PageSize];double IDFk;
} Words[WordSize*10];
struct ednode {char eno[10];double sim;int id;
} Pages[PageSize];
int Trie[4200000][28];              // flag为1说明
int num_word = 0, pos, num_page = 0;
int TNd[PageSize],NUM;
char arr[1024], s[35000000];
int search(char word[]) {               //查找操作,在Trie树中找寻单词int p = 0;for (int i = 0; word[i]; i++) {int ch = word[i] - 'a';if (!Trie[p][ch]) {return 0;}p = Trie[p][ch];}return p;
}
int cmp(const void *a, const void* b)
{struct ednode *tmpa = (struct ednode*)a,*tmpb = (struct ednode*)b;if(tmpa->sim < tmpb->sim) return 1;else if(tmpa->sim > tmpb->sim) return -1;else {return tmpa->id -tmpb->id; }
}
int main(int argc, char *argv[]) {//这一部分是将词典里的词录入到Trie树里,涉及插入操作,但这块的pos值特别大FILE *dictionary = fopen("dictionary.txt", "r");while (fscanf(dictionary, "%s", arr) != EOF) {int p = 0, i;for (i = 0; arr[i] != '\0'; i++) {int ch = arr[i] - 'a';if (!Trie[p][ch]) {Trie[p][ch] = ++pos;}p = Trie[p][ch];}Trie[p][27] = 1;        //用flag标记这个单词是词典中的词}fclose(dictionary);//用stopwords里的单词在Trie树中寻找,找到的将flag变为0,以后就只统计flag为1的单词,涉及查找,用函数searchFILE *stopwords = fopen("stopwords.txt", "r");while (fscanf(stopwords, "%s", arr) != EOF) {int index = search(arr);if (Trie[index][27])Trie[index][27]= 0;}fclose(stopwords);//读入ki部分NUM = atoi(argv[1]);for (int i = 2; i < argc; i++) {int index = search(argv[i]);if(Trie[index][27] == 1)Trie[index][27] = 2;}//将所有内容读入到一个大树组中FILE *article = fopen("article.txt", "r");int c = fread(s, sizeof(char), 35000000, article);fclose(article);//读取articlechar word[85];                                   //word是用来存储文档中的单词for (int i = 0; i<c; i++) {if (i == 0) {                               //将第一页的页码录入,存在数组Pages中,从0开始int j;for (j = i; s[j] != '\n'; j++) {Pages[num_page].eno[j-i] = s[j];}Pages[num_page].id = num_page+1;       //将第几页录入,方便以后排序后能确定正确的顺序} else if ((s[i] <= 'z' && s[i] >= 'a') || (s[i] <= 'Z' && s[i] >= 'A')) {int j;for (j = i;(s[j] <= 'z' && s[j] >= 'a') || (s[j] <= 'Z' && s[j] >= 'A'); j++) {word[j - i] = tolower(s[j]);      //将单词都转换为小写单词}word[j-i]='\0';i = j - 1;int index = search(word);   //找到Trie树中单词对应的位置indexif(Trie[index][27] == 1){TNd[num_page]++;}else if (Trie[index][27] == 2) {          //如果是这个单词是Ki,则在Words中输入该单词,actual对应的是实际单词在Words中的位置Trie[index][27] = 3;          //表示该单词不是第一次出现了Trie[index][26] = num_word++; //actual对应的位置正是最后一位if(Words[Trie[index][26]].TNkd == 0)Words[Trie[index][26]].DNk++;    //      DNk++Words[Trie[index][26]].TNkd++;//单词的TNkd++TNd[num_page]++;} else if (Trie[index][27] == 3) {  //flag为3表示该单词不是第一次出现了,actual是单词在Words中的位置if(Words[Trie[index][26]].TNkd == 0)Words[Trie[index][26]].DNk++;Words[Trie[index][26]].TNkd++;TNd[num_page]++;}} else if (s[i] == '\f') {                  //一页结束,处理如下int j;for(j = 0; j < num_word;j++)          //清算本页的TFkd,并将TNkd归0{Words[j].TFkd[num_page] = 100.0*Words[j].TNkd/TNd[num_page];Words[j].TNkd = 0;}num_page++;                              //页码增加for(j = i+1; !isdigit(s[j]);j++);     //找到页码表示的位置i = j;for(j = i; s[j] != '\n';j++)            //读入新页码{    Pages[num_page].eno[j-i] = s[j];}Pages[num_page].id = num_page+1;}}num_page++;//计算部分for(int i = 0; i < num_word;i++){if(Words[i].DNk!=0)Words[i].IDFk = log10(1.0*num_page/Words[i].DNk);              //计算IDFk}for(int i=0;i< num_page;i++){for(int j = 0; j< num_word;j++){Pages[i].sim += 1.0*Words[j].TFkd[i]*Words[j].IDFk;//计算Sim}}//输出部分FILE *results = fopen("results.txt","w");qsort(Pages,num_page,sizeof(Pages[0]),cmp);for(int i = 0 ; i <NUM && i < num_page;i++){fprintf(results,"%.6lf %d %s\n",Pages[i].sim,Pages[i].id,Pages[i].eno);}for(int i = 0; i < 5 && i <num_page;i++){printf("%.6lf %d %s\n",Pages[i].sim,Pages[i].id,Pages[i].eno);}fclose(results);return 0;
}

2022BUAA数据结构期末大作业的一些想法相关推荐

  1. 数字图像处理期末大作业-美图秀秀

    本项目是以matlab为主语言并设计GUI界面的一款简易美图秀秀,包含基础的图像处理和一些常见美颜算法 对于一些matlab较难实现的算法采用C++或python来实现 ⭐️ github地址:htt ...

  2. 嵌入式期末大作业——家用智能晾衣杆

    嵌入式期末大作业--家用智能晾衣杆 目录 嵌入式期末大作业--家用智能晾衣杆 前言 一.项目介绍 1.项目背景 2.项目构思 二.系统设计 1.系统概述 2.设计思路 3.设计草图 三.硬件设计 1. ...

  3. Xd设计与制作记录贴:记录我的艺术设计基础期末大作业

    Xd设计与制作记录贴:记录我的艺术设计基础期末大作业 通过艺术设计基础这门课的学习,我掌握了一些PPT的制作技巧和Xd入门操作方法. 期末大作业是要根据以前课上讨论的主题做一个能展现自己设计思路的工程 ...

  4. Android app开发期末大作业“快乐七巧板”开发总结

    写在前面 大二的暑假过去一半了,我终于想起来把期末大作业写出来了.本文的主要目的就是总结一学期安卓开发入门和最后期末大作业相关的一些收获和感受. 给自己一个总结和给以后再去学习的相关内容的小伙伴们一咪 ...

  5. 天津理工大学研究生学位课《算法设计与分析》期末大作业

    2022- 2023学年度第一学期 研究生学位课< 算法设计与分析 > 期末大作业 2022级电子信息天理研究生 一.简答题 1.若,写出用Θ.Ω和О描述f(n) 的渐进表达.(7分) 答 ...

  6. C语言期末大作业-学生成绩管理系统(完整源码+设计报告)

    C语言-学生成绩管理系统 一.学生成绩管理系统源码(完整) 二.程序设计报告 1.课程设计目的 2.课程设计任务与要求: 4.课程设计成果(运行截图) 5.课程设计心得 一.学生成绩管理系统源码(完整 ...

  7. 【c语言期末大作业】加强剪切板cDitto的实现

    [c语言期末大作业]cDitto 本人为大一,内容如有错误,欢迎大家的指正,谢谢. 如果涉及侵权等问题,也请联系本人,删除本文. 如果只想看代码,请移步至 三.cDitto的着手开发 -如果想看效果展 ...

  8. 计算机管理信息系统大作业,管理信息系统期末大作业

    <管理信息系统>期末大作业- 1 -计算机科学与工程学院<管理信息系统>期末大作业班 级: 学 号: 姓 名: 指 导 教 师: 杨 剑 勇 2011 年 月计算机科学与工程学 ...

  9. 计算机应用基础考试试题及答案 在word中,用户建立的文件默认,天津大学《计算机应用基础》2016年12月考试期末大作业考核试题...

    请同学及时保存作业,如您在20分钟内不作操作,系统将自动退出. <计算机应用基础>2016年12月考试期末大作业考核试题 试卷总分:100 测试时间:-- 单选题 一.单选题(共50 道试 ...

  10. 东华大学java_东华大学2020秋《Java程序设计》期末大作业

    东华大学继续教育学院 2020年秋季学期 远程学历教育<Java程序设计>期末大作业 一.选择题(本大题共10小题,每小题 1分, 共10分) 1.    下列哪个不是面向对象程序设计的基 ...

最新文章

  1. 本地方法接口和本地方法栈
  2. matlab在线性系统理论中的应用,线性系统理论相关的matlab应用.ppt
  3. SQL Server数据库查询速度慢的原因和解决方法
  4. ubuntu安装软件依赖
  5. Myeclipse+mysql出现中文乱码情况
  6. PXC集群常见错误(一)
  7. Segment-based Disparity Refinement with Occlusion Handling for Stereo Matching翻译和理解
  8. CF1045G AI robots(动态开点线段树)
  9. 人人开源 VUE项目报错./src/assets/scss/index.scssModule build failed: Error: ENOENT: no such file or direct
  10. c语言期末考试复习题,C语言期末考试复习资料 计算机等级考试复习题目
  11. 如何通俗易懂地解释卷积?(2)
  12. Linux下完全删除用户
  13. 不学网php线下培训视频教程
  14. A* 寻路算法[转载]
  15. 新东方报名系统服务器地址,新东方邮箱服务器地址
  16. ava程序员职业生涯规划范文
  17. bshare分享 插件使用教程
  18. 宋朝历史衰败原因大揭秘
  19. 注册mysql确认邮件_如何在MySQL中使用电子邮件ID选择@之前的所有内容?
  20. IMSI号和IMEI解释

热门文章

  1. Mac连局域网打印机
  2. 【Meta Learning学习笔记】Meta Learning详解
  3. zabbix Too many processes on xxx
  4. 数据库迁移测试——功能测试
  5. 2019辽宁公务员考试:申论突破80+学习计划第二步
  6. 数据库系统概论-数据库系统阶段的特点
  7. ffmpeg用法及如何使用fluent-ffmpeg
  8. thinkphp 实现新订单语音提醒功能
  9. DBUtils和连接池的笔记和总结
  10. 微积分Z2J5 两个重要极限