4093:倒排索引查询--bitmap并查集

xiaoxiao2021-02-28  135

总时间限制:1000ms 内存限制: 131072kB 描述 现在已经对一些文档求出了倒排索引,对于一些词得出了这些词在哪些文档中出现的列表。 要求对于倒排索引实现一些简单的查询,即查询某些词同时出现,或者有些词出现有些词不出现的文档有哪些。 输入 第一行包含一个数N,1 <= N <= 100,表示倒排索引表的数目。 接下来N行,每行第一个数ci,表示这个词出现在了多少个文档中。接下来跟着ci个数,表示出现在的文档编号,编号不一定有序。1 <= ci <= 1000,文档编号为32位整数。 接下来一行包含一个数M,1 <= M <= 100,表示查询的数目。 接下来M行每行N个数,每个数表示这个词要不要出现,1表示出现,-1表示不出现,0表示无所谓。数据保证每行至少出现一个1。 输出 共M行,每行对应一个查询。输出查询到的文档编号,按照编号升序输出。 如果查不到任何文档,输出”NOT FOUND”。 样例输入

3 3 1 2 3 1 2 1 3 3 1 1 1 1 -1 0 1 -1 -1

样例输出

NOT FOUND 1 3 1

思路 1 对应每个单词 维护一个文章set集合

n = int(input()) info = dict((i,set()) for i in range(n)) for wordId, articles in info.items(): articles.update(input().split()[1:]) m = int(input()) for _ in range(m): ans = None for wordId, v in enumerate(input().split()): if v == '1': ans = ans.intersection(info[wordId]) if ans is not None else info[wordId] elif v == '-1' and ans: ans = [articleIndex for articleIndex in ans if articleIndex not in info[wordId]] if not ans: print("NOT FOUND") else: ans = list(ans) ans.sort() print(" ".join(ans))

思路二 借用bitmap思想 假定最多10000篇文章 对于第i个word 维护一个 10000个bit位Ai 表示8000篇文章 1–文章包含单词 0 –不包含

算法 查 包含 第i 个单词 和 第j 个单词的集合 A = Ai&Aj 查 包含 第i个单词 但 不包含第j 个单词的集合 A = Ai&(~Aj)

解决问题:

10000个bit 和 文章编号articleId 对应关系;

用int map[10000]来存储 map[i]=articleId ;

位图的存储;

char bit[N][10000/8]; // 第i个word的位图 用10000/8个字节表示 即bit[i];

位图操作; 1、第i个单词 出现在文档map[k]=articleId中, 对应位=1 *((int *)(bit[i])+k/INTBL) |= 1<<(k%INTBL); // eg k=32 32bit个一组 内存分布 00…00 00..01 00..00 2、&操作 以4个字节=1个int型为基本单位进行求&; for(k=0; k<10000/32; k++) ((int *)(bit[i])+k) & ((int *)(bit[j])+k)

代码如下 内存 1632kB 时间 51ms

#include <stdio.h> #include <stdlib.h> #define BYTEL (sizeof(byte)) #define INTL (sizeof(int)) #define INTBL (INTL*BYTEL) #define N 100 #define AN (INTBL*1000) // 文章数量 INTBL的倍数 方便后续处理 char bit[N][AN/BYTEL]; int map[AN]; // 存储文章编号-索引映射; 把文章编号映射为0-AN的索引编号 int ansOrd[AN]; // 有序答案 /* * 打印 地址p 长度为length*8 的内存中的 bit值 */ int pp(char *p, int length) { printf("%d ", p); int i,j, *q=(int *)p; for(i=0; i<length/INTL; i++, q++) { for(j=INTBL-1;j>=0;j--) { if( (*q) & (1<<j) ) printf("1"); else printf("0"); } printf(" "); } printf("\n"); } // 把文章编存放到map中 保证map的长度>=文章数 int getId(int n) { int i=0, begin=0; begin = n%AN; for(i=begin; map[i]!=-1 && map[i]!=n; ) { i = (i+1)%AN; if( i==begin ) return -1; } map[i] = n; return i; //返回对应的索引 } int test4193() { int i, j, k, v, u, n, m; int *p, *q; char ans[AN/BYTEL]; memset(map, -1, sizeof(map)); // 读数据 i = 0; scanf("%d", &n); while( i<n ) { scanf("%d", &c); while( c-- ) { scanf("%d", &k); k = getId(k); //找到文档对应的map索引 // 第i个单词 出现在文档索引k中 *((int *)(bit+i)+k/INTBL) |= 1<<(k%INTBL); } // pp(bit+i, AN/BYTEL); i++; } // 判断 scanf("%d", &m); while(m--) { memset(ans, -1, sizeof(ans)); //默认都符合条件 // 对每一个单词遍历 for(i=0; i<n; i++) { scanf("%d", &k); // 包含; 并操作 if(k==1) for(j=0; j<AN/INTBL; j++) { *((int *)ans+j) &= *((int *)(bit+i)+j); // ans &= bit[i] } // 不包含; 取反后 求并集 else if(k==-1) for(j=0; j<AN/INTBL; j++) { *((int *)ans+j) &= ~ *((int *)(bit+i)+j); // ans &= ~ bit[i] } } // 统计答案 u=-1; // pp((char*)ans, sizeof(ans)); //显示内存bit值 for(i=0, p=(int *)ans; i<AN/INTBL; i++, p++) { for(j=0;j<INTBL;j++) //对每一位遍历 { // bit=1时 把对应的文章编号有序插入ansOrd中 if( (*p) & (1<<j) ) { v=map[i*INTBL+j]; k=u; while( k>=0 && ansOrd[k] > v ) { ansOrd[k+1] = ansOrd[k]; k--; } ansOrd[k+1]=v; u++; } } } // 打印答案 if(u==-1) { printf("NOT FOUND\n"); } else { printf("%d", ansOrd[0]); for(k=1; k<=u; k++) printf(" %d", ansOrd[k]); printf("\n"); } } }
转载请注明原文地址: https://www.6miu.com/read-84122.html

最新回复(0)