PTA 数据结构与算法 7-44 基于词频的文件相似度

如有不对,不吝赐教
进入正题:

实现一种简单原始的文件相似度计算,即以两文件的公共词汇占总词汇的比例来定义相似度。为简化问题,这里不考虑中文(因为分词太难了),只考虑长度不小于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%

这道题目其实就是Hash表的应用,但是有两个点需要注意一下。

第一个就是单词的判定,这里的单词判定由题中给出,要切记:

只包括仅由英文字母组成的、长度不小于3、且不超过10的英文单词,长度超过10的只考虑前10个字母
大小写不同的同一单词被认为是相同的单词,例如“You”和“you”是同一个单词

所以说"a"就不能算一个单词,而"aaaaaaaaaa"和"aaaaaaaaaaaaaaaa"是同一个单词

然后就是重复率的计算,计算是这样的:

假定第一个文件话中有A个单词,第二个文件话中有B个单词,他们有C个重复的单词,那么
重复率是C/(A+B-C)

然后我的思路是为每一个文件建立一个Hash表,然后匹配的时候是用链表匹配的,其实都用Hash表会更好。

下面给出代码:

#include<stdio.h>
#include<malloc.h>
#include<string.h>

struct WordList{
struct WordNode *head;
struct WordNode *tail;
};

struct WordNode{
char word[11];    //最多有10个字符
struct WordNode *next;
};

struct HashList{
char word[11];
struct HashList *next;
};

struct File{
struct WordList *word;      //单词链表
struct HashList *table;      //分配成数组 大小为allword*2
short set;              //该文件是否建立了Hash表
short allword;          //该文件的单词总数
};

short Hash(char *str,short N);
void GetFile(struct File *f);
struct WordList *InsertWL(struct WordList *word,char *str);
struct HashList InsertHL(struct HashList *HL,char *word);
short Cmp(struct File *f,struct WordList *word);
struct File *CreatHashTable(struct File *f);

int main(void)
{
    short N,M;
    short i;
    short f1,f2;      //比较的两个文档
    short same;       //两个文档的相同的单词数目
    fscanf(stdin,"%hd",&N);
    struct File *f[N];

    for(i=0;i<N;i++){
      f[i]=(struct File *)malloc(sizeof(struct File));
      f[i]->allword=0;
      f[i]->set=0;
      f[i]->word=(struct WordList *)malloc(sizeof(struct WordList));
      f[i]->word->head=f[i]->word->tail=NULL;
      f[i]->table=NULL;
    }

    for(i=0;i<N;i++){
      GetFile(f[i]);                  //建立文档单词库
      if(f[i]->allword)
        CreatHashTable(f[i]);
    }

    fscanf(stdin,"%hd",&M);
    for(i=0;i<M;i++){
      fscanf(stdin,"%hd %hd",&f1,&f2);
      if(!f[f1-1]->allword||!f[f2-1]->allword)
        same=0;                   //二者有一个为0就不用在测试了
      else{
        if(f[f1-1]->allword>f[f2-1]->allword)
          same=Cmp(f[f1-1],f[f2-1]->word);
        else
          same=Cmp(f[f2-1],f[f1-1]->word);
      }         //用词汇少的作为被比较的 节省时间
      if(!same)
        printf("0.0%%\n");
      else
        printf("%.1f%%\n",100.0*same/(f[f1-1]->allword+f[f2-1]->allword-same));
    }

    return 0;
}

void GetFile(struct File *f)
{
    char ch;
    char str[11];
    short i;

    ch=fgetc(stdin);
    while(!(ch<='z'&&ch>='a')&&!(ch<='Z'&&ch>='A'))
      ch=fgetc(stdin);
    while(ch){     //以换行和文件尾结束
      if(ch=='\n'){
        ch=fgetc(stdin);
        if('#'==ch){
          ch=fgetc(stdin);
          if('\n'==ch||!ch)
            break;
        }
      }
      while((!(ch<='z'&&ch>='a')&&!(ch<='Z'&&ch>='A'))&&'\n'!=ch&&ch)
        ch=fgetc(stdin);           //除去非英文字符
      if('\n'==ch)
        continue;
      i=0;
      do{
        str[i]=ch;
        if(str[i]>='A'&&str[i]<='Z')
          str[i]=str[i]-'A'+'a';     //改为小写字母
        i++;
        ch=fgetc(stdin);
      }while(((ch<='z'&&ch>='a')||(ch<='Z'&&ch>='A'))&&i<11);
      if(i<3)
        continue;
      if(i>10){
        str[10]='\0';
        while(((ch<='z'&&ch>='a')||(ch<='Z'&&ch>='A')))
          ch=fgetc(stdin);       //除去大于10个字符长度单词后的多余字符
      }
      else
        str[i]='\0';      //结束符号
      if(InsertWL(f->word,str))      //加入单词表成功
        f->allword++;
    }

    return ;
}

struct WordList *InsertWL(struct WordList *word,char *str)
{
    struct WordNode *cur=word->head;
    struct WordNode *newOne;
    newOne=(struct WordNode *)malloc(sizeof(struct WordNode));
    newOne->next=NULL;
    strcpy(newOne->word,str);

    if(!word->head){
      word->head=newOne;
      word->tail=newOne;
      return word;
    }

    if(!strcmp(cur->word,str)){
      free(newOne);
      newOne=NULL;
      return NULL;
    }

    while(cur&&strcmp(cur->word,str))
      cur=cur->next;

    if(cur){
      free(newOne);
      newOne=NULL;
      return NULL;
    }          //已存在该单词

    word->tail->next=newOne;
    word->tail=newOne;

    return word;
}

short Hash(char *str,short N)
{
    short key=0;
    short seed=131;

    while(*str){
      key=key*seed+(*str++);
      if(key>=N)
        key=key%N;
        
      while(key<0)
        key+=N;
    }

    return key;
}        //字符串Hash

short Cmp(struct File *f,struct WordList *word)
{
    short same=0;
    short size=f->allword*2;
    short key;
    struct WordNode *curWord=word->head;
    struct HashList *curHash;

    while(curWord){
      key=Hash(curWord->word,size);
      curHash=&(f->table[key]);
      while(curHash&&strcmp(curHash->word,curWord->word))
        curHash=curHash->next;
      if(curHash)
        same++;
      curWord=curWord->next;
    }

    return same;
}

struct File *CreatHashTable(struct File *f)
{
    struct WordNode *cur=f->word->head;
    short key;
    short size=f->allword*2;
    short i;

    f->set=1;
    f->table=(struct HashList *)malloc(size*sizeof(struct HashList));

    for(i=0;i<size;i++){
      f->table[i].next=NULL;
      f->table[i].word[0]='\0';
    }

    while(cur){
      key=Hash(cur->word,size);
      f->table[key]=InsertHL(&(f->table[key]),cur->word);
      cur=cur->next;
    }

    return f;
}

struct HashList InsertHL(struct HashList *HL,char *word)
{
    struct HashList *cur=HL;
    struct HashList *newOne;
    newOne=(struct HashList *)malloc(sizeof(struct HashList));
    strcpy(newOne->word,word);
    newOne->next=NULL;

    if(!cur->word[0]){
      HL=newOne;
      return *HL;
    }

    while(cur->next)
      cur=cur->next;
    cur->next=newOne;

    return *HL;
}

测试结果:
在这里插入图片描述

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值