跳转至

1152. 用户网站访问行为分析 🔒

题目描述

给定两个字符串数组 username 和 website 和一个整数数组 timestamp 。给定的数组长度相同,其中元组 [username[i], website[i], timestamp[i]] 表示用户 username[i] 在时间 timestamp[i] 访问了网站 website[i] 。

访问模式 是包含三个网站的列表(不一定是完全不同的)。

  • 例如,["home", "away", "love"]["leetcode", "love", "leetcode"],和 ["luffy", "luffy", "luffy"] 都是模式。

一种 访问模式得分 是访问该模式中所有网站的用户数量,这些网站在该模式中出现的顺序相同。

  • 例如,如果模式是 [“home”,“away”,“love”] ,那么分数就是用户数量 x , x 访问了 “home” ,然后访问了 “away” ,然后访问了 “love”
  • 同样,如果模式是 ["leetcode", "love", "leetcode"] ,那么分数就是用户数量 x ,使得 x 访问了"leetcode",然后访问了 "love" ,之后又访问了 "leetcode"
  • 另外,如果模式是 [“luffy”,“luffy”,“luffy”] ,那么分数就是用户数量 x ,这样 x 就可以在不同的时间戳上访问 “luffy” 三次。

返回 得分 最大的 访问模式 。如果有多个访问模式具有相同的最大分数,则返回字典序最小的。

请注意,模式中的网站不需要连续访问,只需按照模式中出现的顺序访问即可。

 

示例 1:

输入:username = ["joe","joe","joe","james","james","james","james","mary","mary","mary"], timestamp = [1,2,3,4,5,6,7,8,9,10], website = ["home","about","career","home","cart","maps","home","home","about","career"]
输出:["home","about","career"]
解释:本例中的元组是:
["joe","home",1],["joe","about",2],["joe","career",3],["james","home",4],["james","cart",5],["james","maps",6],["james","home",7],["mary","home",8],["mary","about",9] 和 ["mary","career",10]。
模式 ("home", "about", "career") 的得分为 2(joe 和 mary)。
模式 ("home", "cart", "maps") 的得分为 1 (james).
模式 ("home", "cart", "home") 的得分为 1 (james).
模式 ("home", "maps", "home") 的得分为 1 (james).
模式 ("cart", "maps", "home") 的得分为 1 (james).
模式 ("home", "home", "home") 的得分为 0(没有用户访问过 home 3次)。

示例 2:

输入: username = ["ua","ua","ua","ub","ub","ub"], timestamp = [1,2,3,4,5,6], website = ["a","b","a","a","b","c"]
输出: ["a","b","a"]

 

提示:

  • 3 <= username.length <= 50
  • 1 <= username[i].length <= 10
  • timestamp.length == username.length
  • 1 <= timestamp[i] <= 109
  • website.length == username.length
  • 1 <= website[i].length <= 10
  • username[i] 和 website[i] 都只含小写字符
  • 它保证至少有一个用户访问了至少三个网站
  • 所有元组 [username[i], timestamp[i], website[i] 均 不重复

解法

方法一:哈希表 + 排序

我们先用哈希表 $d$ 记录每个用户访问的网站,然后遍历 $d$,对于每个用户,我们枚举其访问的所有三元组,统计去重三元组的出现次数,最后遍历所有三元组,返回出现次数最多的、字典序最小的三元组。

时间复杂度 $O(n^3)$,空间复杂度 $O(n^3)$。其中 $n$ 是 username 的长度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
    def mostVisitedPattern(
        self, username: List[str], timestamp: List[int], website: List[str]
    ) -> List[str]:
        d = defaultdict(list)
        for user, _, site in sorted(
            zip(username, timestamp, website), key=lambda x: x[1]
        ):
            d[user].append(site)

        cnt = Counter()
        for sites in d.values():
            m = len(sites)
            s = set()
            if m > 2:
                for i in range(m - 2):
                    for j in range(i + 1, m - 1):
                        for k in range(j + 1, m):
                            s.add((sites[i], sites[j], sites[k]))
            for t in s:
                cnt[t] += 1
        return sorted(cnt.items(), key=lambda x: (-x[1], x[0]))[0][0]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {
    public List<String> mostVisitedPattern(String[] username, int[] timestamp, String[] website) {
        Map<String, List<Node>> d = new HashMap<>();
        int n = username.length;
        for (int i = 0; i < n; ++i) {
            String user = username[i];
            int ts = timestamp[i];
            String site = website[i];
            d.computeIfAbsent(user, k -> new ArrayList<>()).add(new Node(user, ts, site));
        }
        Map<String, Integer> cnt = new HashMap<>();
        for (var sites : d.values()) {
            int m = sites.size();
            Set<String> s = new HashSet<>();
            if (m > 2) {
                Collections.sort(sites, (a, b) -> a.ts - b.ts);
                for (int i = 0; i < m - 2; ++i) {
                    for (int j = i + 1; j < m - 1; ++j) {
                        for (int k = j + 1; k < m; ++k) {
                            s.add(sites.get(i).site + "," + sites.get(j).site + ","
                                + sites.get(k).site);
                        }
                    }
                }
            }
            for (String t : s) {
                cnt.put(t, cnt.getOrDefault(t, 0) + 1);
            }
        }
        int mx = 0;
        String t = "";
        for (var e : cnt.entrySet()) {
            if (mx < e.getValue() || (mx == e.getValue() && e.getKey().compareTo(t) < 0)) {
                mx = e.getValue();
                t = e.getKey();
            }
        }
        return Arrays.asList(t.split(","));
    }
}

class Node {
    String user;
    int ts;
    String site;

    Node(String user, int ts, String site) {
        this.user = user;
        this.ts = ts;
        this.site = site;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Solution {
public:
    vector<string> mostVisitedPattern(vector<string>& username, vector<int>& timestamp, vector<string>& website) {
        unordered_map<string, vector<pair<int, string>>> d;
        int n = username.size();
        for (int i = 0; i < n; ++i) {
            auto user = username[i];
            int ts = timestamp[i];
            auto site = website[i];
            d[user].emplace_back(ts, site);
        }
        unordered_map<string, int> cnt;
        for (auto& [_, sites] : d) {
            int m = sites.size();
            unordered_set<string> s;
            if (m > 2) {
                sort(sites.begin(), sites.end());
                for (int i = 0; i < m - 2; ++i) {
                    for (int j = i + 1; j < m - 1; ++j) {
                        for (int k = j + 1; k < m; ++k) {
                            s.insert(sites[i].second + "," + sites[j].second + "," + sites[k].second);
                        }
                    }
                }
            }
            for (auto& t : s) {
                cnt[t]++;
            }
        }
        int mx = 0;
        string t;
        for (auto& [p, v] : cnt) {
            if (mx < v || (mx == v && t > p)) {
                mx = v;
                t = p;
            }
        }
        return split(t, ',');
    }

    vector<string> split(string& s, char c) {
        vector<string> res;
        stringstream ss(s);
        string t;
        while (getline(ss, t, c)) {
            res.push_back(t);
        }
        return res;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func mostVisitedPattern(username []string, timestamp []int, website []string) []string {
    d := map[string][]pair{}
    for i, user := range username {
        ts := timestamp[i]
        site := website[i]
        d[user] = append(d[user], pair{ts, site})
    }
    cnt := map[string]int{}
    for _, sites := range d {
        m := len(sites)
        s := map[string]bool{}
        if m > 2 {
            sort.Slice(sites, func(i, j int) bool { return sites[i].ts < sites[j].ts })
            for i := 0; i < m-2; i++ {
                for j := i + 1; j < m-1; j++ {
                    for k := j + 1; k < m; k++ {
                        s[sites[i].site+","+sites[j].site+","+sites[k].site] = true
                    }
                }
            }
        }
        for t := range s {
            cnt[t]++
        }
    }
    mx, t := 0, ""
    for p, v := range cnt {
        if mx < v || (mx == v && p < t) {
            mx = v
            t = p
        }
    }
    return strings.Split(t, ",")
}

type pair struct {
    ts   int
    site string
}

评论