LeetCode 面试题 17.26. 稀疏相似度

地址 https://leetcode-cn.com/problems/sparse-similarity-lcci/

两个(具有不同单词的)文档的交集(intersection)中元素的个数除以并集(union)中元素的个数,就是这两个文档的相似度。例如,{1, 5, 3} 和 {1, 7, 2, 3} 的相似度是 0.4,其中,交集的元素有 2 个,并集的元素有 5 个。给定一系列的长篇文档,每个文档元素各不相同,并与一个 ID 相关联。它们的相似度非常“稀疏”,也就是说任选 2 个文档,相似度都很接近 0。请设计一个算法返回每对文档的 ID 及其相似度。只需输出相似度大于 0 的组合。请忽略空文档。为简单起见,可以假定每个文档由一个含有不同整数的数组表示。

输入为一个二维数组 docs,docs[i] 表示 id 为 i 的文档。返回一个数组,其中每个元素是一个字符串,代表每对相似度大于 0 的文档,其格式为 {id1},{id2}: {similarity},其中 id1 为两个文档中较小的 id,similarity 为相似度,精确到小数点后 4 位。以任意顺序返回数组均可。

示例:

输入: 
[
  [14, 15, 100, 9, 3],
  [32, 1, 9, 3, 5],
  [15, 29, 2, 6, 8, 7],
  [7, 10]
]
输出:
[
  "0,1: 0.2500",
  "0,2: 0.1000",
  "2,3: 0.1429"
]
提示:

docs.length <= 500
docs[i].length <= 500
相似度大于 0 的文档对数不会超过 1000

解法  

我们要使用哈希加速搜索比对的效率

如果能快速的知道 某两个不同文档的交集数目 就比较好做了

如何知道某个单词被那些文档包含呢?  遍历单词元素 依次去查询文档是否包含是最朴素的做法,但是效率太低。又可以考虑哈希加速保存和搜索

两重哈希的做法如下

首先 使用map<int, vector<int>> doc_idx 记录 某个单词被那些文档记录。

然后使用一个稀疏矩阵的数据结构vector<unordered_map<int, int>> graph 记录 某两个文档有多少个相同的单词数目

代码如下

unordered_map<int, vector<int>> doc_idx;
    for (int i = 0; i < docs.size(); i++) {
        for (auto& e : docs[i]) {
            doc_idx[e].push_back(i);
        }
    }
    vector<unordered_map<int, int>> gra(510);    //稀疏矩阵图结构

    for (auto& e : doc_idx) {
        vector<int> &p = e.second;
        for (int i = 0; i < p.size(); i++) {
            for(int j = i+1;j < p.size();j++){
            int a = p[i]; int b = p[j];
            gra[a][b]++; gra[b][a]++;
            }
        }
    }

两个结构的意义如下

doc_idx{7,{2,3,5,7}} 表示 7号单词被文档2, 3,5,7 同时拥有 

gra{{2,3},5} 表示文档2,3拥有的相同单词数目为5个  也就是该两个文档的交集

那么 文档 2 3 的并集合数目 就是  文档[2]单词数目 + 文档[3]单词数目- 文档23交集数目 

全部代码如下

class Solution {
public:
   
   
   vector<string> computeSimilarities(vector<vector<int>>& docs) {
    vector<string> vs;
    unordered_map<int, vector<int>> doc_idx;
    for (int i = 0; i < docs.size(); i++) {
        for (auto& e : docs[i]) {
            doc_idx[e].push_back(i);
        }
    }
    vector<unordered_map<int, int>> gra(510);    //稀疏矩阵图结构

    for (auto& e : doc_idx) {
        vector<int> &p = e.second;
        for (int i = 0; i < p.size(); i++) {
            for(int j = i+1;j < p.size();j++){
            int a = p[i]; int b = p[j];
            gra[a][b]++; gra[b][a]++;
            }
        }
    }

    for (int i = 0; i < docs.size(); i++) {
        for (int j = i + 1; j < docs.size(); j++) {
            if (gra[i][j] != 0) {
                int num = docs[i].size() + docs[j].size() - gra[i][j];
                double res = gra[i][j] * 1.0 / num;
                char s[20];
                sprintf(s, "%d,%d: %.4f", i, j, res + 1e-9);
                //cout << s << endl;
                vs.push_back(string(s, s + 20));
            }
        }
    }

    return vs;
}


};

最后要注意的是 由于数据精度问题 最后计算出来的结果要加上1e-9 才和答案一致

作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
阿里打赏 微信打赏
原文地址:https://www.cnblogs.com/itdef/p/12821239.html