[leetcode 双周赛 6] 1152 用户网站访问行为分析
1152 Analyze User Website Visit Pattern 用户网站访问行为分析
描述
为了评估某网站的用户转化率,我们需要对用户的访问行为进行分析,并建立用户行为模型。
日志文件
中已经记录了用户名
、访问时间
以及页面路径
。
为了方便分析,日志文件中的 N 条记录已经被解析成三个长度相同且长度都为 N 的数组,分别是:用户名 username
,访问时间 timestamp
和 页面路径 website
。第 i 条记录意味着用户名是 username[i] 的用户在 timestamp[i] 的时候访问了路径为 website[i] 的页面。
我们需要找到用户访问网站时的 『共性行为路径』
,也就是有最多的用户都 至少按某种次序访问过一次 的三个页面路径。需要注意的是,用户 可能不是连续访问 这三个路径的。
『共性行为路径』是一个 长度为 3 的页面路径列表,列表中的路径 不必不同,并且按照访问时间的先后升序排列。
如果有多个满足要求的答案,那么就请返回按字典序排列最小的那个。
(页面路径列表 X 按字典序小于 Y 的前提条件是:X[0] < Y[0] 或 X[0] == Y[0] 且 (X[1] < Y[1] 或 X[1] == Y[1] 且 X[2] < Y[2]))
题目保证一个用户会至少访问 3 个路径一致的页面,并且一个用户不会在同一时间访问两个路径不同的页面。
- 示例:
输入: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", 1, "home"]
["joe", 2, "about"]
["joe", 3, "career"]
["james", 4, "home"]
["james", 5, "cart"]
["james", 6, "maps"]
["james", 7, "home"]
["mary", 8, "home"]
["mary", 9, "about"]
["mary", 10, "career"]
有 2 个用户至少访问过一次 ("home", "about", "career")。
有 1 个用户至少访问过一次 ("home", "cart", "maps")。
有 1 个用户至少访问过一次 ("home", "cart", "home")。
有 1 个用户至少访问过一次 ("home", "maps", "home")。
有 1 个用户至少访问过一次 ("cart", "maps", "home")。
- 提示:
3 <= N = username.length = timestamp.length = website.length <= 50
1 <= username[i].length <= 10
0 <= timestamp[i] <= 10^9
1 <= website[i].length <= 10
username[i] 和 website[i] 都只含小写字符
思路
暴力枚举每个用户的顺序网页访问路径
(有三个页面)
选出访问路径
出现最多次数的那个 或者 字典序排列最小的那个
代码实现
重要存储结构示意图如下:
暴力枚举每一用户访问路径并投票
class Solution {
public List<String> mostVisitedPattern(String[] username, int[] timestamp, String[] website) {
// utwRecords 根据用户名username组合三个数组 <username, <timestamp, website>>
// 注意内部使用TreeMap 是为了排序 时间戳timestamp的按顺序排列
HashMap<String, TreeMap<Integer, String>> utwRecords = new HashMap<>();
for (int i = 0; i < username.length; i++) {
if (!utwRecords.containsKey(username[i])) {
utwRecords.put(username[i], new TreeMap<Integer, String>());
}
utwRecords.get(username[i]).put(timestamp[i], website[i]);
}
// uwRecords 提取utwRecords中除时间戳timestamp的数据
// 用户名username以及对应访问的网页website(按时间戳排序)
HashMap<String, ArrayList<String>> uwRecords = new HashMap<>();
for (String name : utwRecords.keySet()) {
uwRecords.put(name, new ArrayList<String>());
for (Integer time : utwRecords.get(name).keySet()) {
uwRecords.get(name).add(utwRecords.get(name).get(time));
}
}
// cntWP 存储所枚举的每一条网页访问路径 并计录其出现的次数
// <网页访问路径(3个网页名称合成1条字符串), 出现次数>
HashMap<String, Integer> cntWP = new HashMap<>();
// maxCnt 网页访问路径出现最多次数为
int maxCnt = 0;
// res 最多次数的网页访问路径(3个网页名称合成1条字符串)
String res = new String();
for (String name : uwRecords.keySet()) {
// len 当前用户所访问的网页数
int len = uwRecords.get(name).size();
// single 存储当前用户所访问网页的访问路径 并去重
HashSet<String> single = new HashSet<>();
for (int i = 0; i < len-2; i++) {
for (int j = i+1; j < len-1; j++) {
for (int k = j+1; k < len; k++) {
// 网页访问路径格式: A-->B-->C
String cur = (new StringBuilder()).append(uwRecords.get(name).get(i))
.append("->")
.append(uwRecords.get(name).get(j))
.append("->")
.append(uwRecords.get(name).get(k)).toString();
single.add(cur);
}
}
}
// 遍历当前用户所访问网络的路径 存储并给访问路径计数
for (String str : single) {
if (!cntWP.containsKey(str)) {
cntWP.put(str, 0);
}
cntWP.put(str, cntWP.get(str)+1);
int curCnt = cntWP.get(str);
// 当该访问路径出现次数curCnt大于最大访问次数maxCnt
// 那么结果路径res就是该路径
// 或者
// 当该访问路径出现次数curCnt等于最大访问次数maxCnt 且字典序小于原出现次数最多的路径res
// 那么结果路径res就是该路径
if (curCnt > maxCnt | (curCnt == maxCnt && str.compareTo(res) < 0)) {
maxCnt = curCnt;
res = str;
}
}
}
// ans 存储所求结果访问路径的3个网页
// 其实也可以使用 Arrays.asList(res.split("->"))
ArrayList<String> ans = new ArrayList<>();
for (String str : res.split("->")) {
ans.add(str);
}
return ans;
}
}