日志文件异常检测(Py/Java/C/C++/Js/Go)
华为OD机试真题 华为OD上机考试真题 4月29号 100分题型
华为OD机试真题目录点击查看: 华为OD机试真题题库目录|机考题库 + 算法考点详解
题目描述
在某系统的日志监控服务中,需要实时检测日志文件中的异常模式。系统每天会产生大量日志记录,每条日志记录包含一个字符串标识。异常模式定义为:在同一时间段内,至少出现 3 次的日志标识。
请编写一个程序,找出所有出现次数大于等于 3 次的日志标识,并按照以下规则输出:
- 首先按照出现次数从高到低排序
- 如果出现次数相同,则按照首次出现的先后顺序排序
输入描述
输入所有日志标识以空格分割。
日志标识由大小写字母、数字和下划线组成,长度不超过 50。日志数量范围为[1,10000]
输出描述
输出所有出现次数大于等于 3 次的日志标识,每行一个,按照上述排序规则输出.以空格分割。
如果没有符合条件的日志标识,则输出 "NONE"
用例1
输入
none
error_404 error_404 warning_500 error_404 info_200 warning_500 warning_500 info_200
输出
none
error_404 warning_500
说明
- error_404出现了 3次(数组第0、1、3个元素),首次出现位置是第0个元素
- warning_500 出现了 3 次(数组第2、5、6个元素),首次出现位置是第2个元素
- info_200 只出现了 2 次(数组第4、7个元素),不符合条件
- error_404 和 warning_500 出现次数相同(都是3次),按照首次出现的先后顺序,先输出 error_404,再输出 warning_500, 注意:虽然 info_200 出现了 2 次,但题目要求的是出现次数大于等于 3 次,因此不输出 info_200
用例2
输入
none
test_case test_case test_case2 test_case2 test_case2
输出
none
test_case2
说明
test_case 出现了 2 次,不符合条件
test_case2 出现了 3 次,首次出现位置是数组第3个元素
题解
思路:数据结构运用 + 自定义排序
- 首先使用两个哈希表
logcount,logFirstPos或者类似结构分别统计每个日志出现的次数和首次出现位置。 - 利用1中统计信息,筛选出次数大于等于3的日志放入结果数组中。
- 将结果数组中日志按照
首先按照出现次数从高到低排序,如果出现次数相同,则按照首次出现的先后顺序排序规则进行自定义排序。 - 返回结果,根据题意进行输出即可。
c++
c++
#include<iostream>
#include<vector>
#include<string>
#include <utility>
#include <sstream>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<string> split(const string& str, const string& delimiter) {
vector<string> result;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != string::npos) {
result.push_back(str.substr(start, end - start));
start = end + delimiter.length();
end = str.find(delimiter, start);
}
// 添加最后一个部分
result.push_back(str.substr(start));
return result;
}
vector<string> findAnomalyLogs(vector<string>& records) {
// 出现次数
map<string,int> logcount;
// 首次出现位置
map<string, int> logFirstPos;
for (int i = 0 ; i < records.size(); i++) {
string record = records[i];
logcount[record]++;
if (logFirstPos.find(record) == logFirstPos.end()) {
logFirstPos[record] = i;
}
}
// 筛选出出现次数大于等于3
vector<string> ans;
for (auto &p : logcount) {
if (p.second >= 3) {
ans.push_back(p.first);
}
}
// 自定义排序,先按次数,次数相同按第一次出现位置
sort(ans.begin(), ans.end(), [&](string&a , string& b) {
if (logcount[a] != logcount[b]) {
return logFirstPos[a] < logFirstPos[b];
}
return logcount[a] > logcount[b];
});
return ans;
}
int main() {
string input;
getline(cin, input);
vector<string> records = split(input, " ");
vector<string> ans = findAnomalyLogs(records);
// 输出结果
if (ans.empty()) {
cout << "NONE";
return 0;
}
for (int i = 0; i < ans.size(); i++) {
cout << ans[i];
if (i != ans.size() - 1) {
cout << " ";
}
}
return 0;
}
JAVA
JAVA
import java.util.*;
import java.io.*;
// 主类
public class Main {
// 查找异常日志
public static List<String> findAnomalyLogs(List<String> records) {
// 出现次数
Map<String, Integer> logcount = new HashMap<>();
// 首次出现位置
Map<String, Integer> logFirstPos = new HashMap<>();
for (int i = 0; i < records.size(); i++) {
String record = records.get(i);
logcount.put(record, logcount.getOrDefault(record, 0) + 1);
if (!logFirstPos.containsKey(record)) {
logFirstPos.put(record, i);
}
}
// 筛选出出现次数大于等于3
List<String> ans = new ArrayList<>();
for (String key : logcount.keySet()) {
if (logcount.get(key) >= 3) {
ans.add(key);
}
}
// 自定义排序:先按次数降序,次数相同按首次位置升序
ans.sort((a, b) -> {
if (!logcount.get(a).equals(logcount.get(b))) {
return logcount.get(b) - logcount.get(a);
}
return logFirstPos.get(a) - logFirstPos.get(b);
});
return ans;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = br.readLine();
List<String> records = Arrays.asList(input.split(" "));
List<String> ans = findAnomalyLogs(records);
// 输出结果
if (ans.isEmpty()) {
System.out.print("NONE");
return;
}
for (int i = 0; i < ans.size(); i++) {
System.out.print(ans.get(i));
if (i != ans.size() - 1) {
System.out.print(" ");
}
}
}
}
Python
python
import sys
# 查找异常日志
def findAnomalyLogs(records):
# 出现次数
logcount = {}
# 首次出现位置
logFirstPos = {}
for i, record in enumerate(records):
logcount[record] = logcount.get(record, 0) + 1
if record not in logFirstPos:
logFirstPos[record] = i
# 筛选出出现次数大于等于3
ans = [k for k, v in logcount.items() if v >= 3]
# 自定义排序:先按次数降序,再按首次位置升序
ans.sort(key=lambda x: (-logcount[x], logFirstPos[x]))
return ans
# 读取输入
input_line = sys.stdin.readline().strip()
records = input_line.split()
ans = findAnomalyLogs(records)
# 输出结果
if not ans:
print("NONE")
else:
print(" ".join(ans))
JavaScript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 查找异常日志
function findAnomalyLogs(records) {
// 出现次数
const logcount = new Map();
// 首次出现位置
const logFirstPos = new Map();
for (let i = 0; i < records.length; i++) {
let record = records[i];
logcount.set(record, (logcount.get(record) || 0) + 1);
if (!logFirstPos.has(record)) {
logFirstPos.set(record, i);
}
}
// 筛选
let ans = [];
for (let [k, v] of logcount.entries()) {
if (v >= 3) ans.push(k);
}
// 排序:先按次数降序,再按首次位置升序
ans.sort((a, b) => {
if (logcount.get(a) !== logcount.get(b)) {
return logcount.get(b) - logcount.get(a);
}
return logFirstPos.get(a) - logFirstPos.get(b);
});
return ans;
}
rl.on('line', function(line) {
let records = line.trim().split(" ");
let ans = findAnomalyLogs(records);
if (ans.length === 0) {
console.log("NONE");
} else {
console.log(ans.join(" "));
}
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strings"
)
// 查找异常日志
func findAnomalyLogs(records []string) []string {
// 出现次数
logcount := make(map[string]int)
// 首次出现位置
logFirstPos := make(map[string]int)
for i, record := range records {
logcount[record]++
if _, ok := logFirstPos[record]; !ok {
logFirstPos[record] = i
}
}
// 筛选
ans := []string{}
for k, v := range logcount {
if v >= 3 {
ans = append(ans, k)
}
}
// 排序
sort.Slice(ans, func(i, j int) bool {
a, b := ans[i], ans[j]
if logcount[a] != logcount[b] {
return logcount[a] > logcount[b] // 次数降序
}
return logFirstPos[a] < logFirstPos[b] // 位置升序
})
return ans
}
func main() {
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
records := strings.Split(input, " ")
ans := findAnomalyLogs(records)
// 输出
if len(ans) == 0 {
fmt.Print("NONE")
return
}
for i, v := range ans {
if i > 0 {
fmt.Print(" ")
}
fmt.Print(v)
}
}
C语言
cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXN 10000
#define MAXLEN 100
// 查找字符串是否存在
int findIndex(char arr[][MAXLEN], int size, char *target) {
for (int i = 0; i < size; i++) {
if (strcmp(arr[i], target) == 0) return i;
}
return -1;
}
// 全局变量(供 qsort 比较函数使用)
int countArr[MAXN];
int firstPosArr[MAXN];
// qsort 比较函数
// 排序规则:
// 1️ 次数降序
// 次数相同 → 首次出现位置升序
int cmp(const void *a, const void *b) {
int i = *(int *)a;
int j = *(int *)b;
if (countArr[i] != countArr[j]) {
return countArr[j] - countArr[i]; // 次数降序
}
return firstPosArr[i] - firstPosArr[j]; // 位置升序
}
int main() {
char input[1000000];
fgets(input, sizeof(input), stdin);
// 去掉换行
input[strcspn(input, "\n")] = 0;
char *tokens[MAXN];
int tokenCount = 0;
// 使用 strtok 分割
char *p = strtok(input, " ");
while (p) {
tokens[tokenCount++] = p;
p = strtok(NULL, " ");
}
char unique[MAXN][MAXLEN];
int size = 0;
// 统计次数 + 首次位置
for (int i = 0; i < tokenCount; i++) {
int idx = findIndex(unique, size, tokens[i]);
if (idx == -1) {
strcpy(unique[size], tokens[i]);
countArr[size] = 1;
firstPosArr[size] = i;
size++;
} else {
countArr[idx]++;
}
}
// 收集答案索引
int ansIdx[MAXN];
int ansSize = 0;
for (int i = 0; i < size; i++) {
if (countArr[i] >= 3) {
ansIdx[ansSize++] = i;
}
}
// 使用 qsort 排序
qsort(ansIdx, ansSize, sizeof(int), cmp);
// 输出
if (ansSize == 0) {
printf("NONE");
return 0;
}
for (int i = 0; i < ansSize; i++) {
printf("%s", unique[ansIdx[i]]);
if (i != ansSize - 1) printf(" ");
}
return 0;
}