【备战秋招】每日一题:2023.05.24-华为OD机试第二题-海量日志抑制

为了更好的阅读体检,可以查看我的算法学习博客
在线评测链接:P1301

题目描述

塔子哥的朋友是一位从事运维工作的专业人士。有一天,他突然有急事需要请假,但是他非常担心公司的系统运行日志出现海量日志的问题。这种问题是指系统打印了大量相同或相似内容的日志,导致有效信息难以被捕捉,甚至会影响系统的运行效率。为了避免这种情况发生,塔子哥的朋友请求塔子哥帮助管理系统的运行日志,并确保只记录有用的信息,避免无效日志的产生。对于运维而言,系统的运行日志是非常重要的,因为它包含了系统运行时的各种细节和提示信息,能够帮助运维人员诊断和解决各种问题。

塔子哥针对海量日志的问题,提出了一种智能算法机制。避免在系统运行时产生大量日志。在这个问题中,我们将"海量日志"定义如下:在 10 10 10毫秒内(小于 10 10 10毫秒),如果打印了 2 2 2条相同的日志,只保留第一条;在 100 100 100 毫秒内(小于 100 100 100毫秒),如果打印了 10 10 10条相似的日志,只保留前9条。按时间读取日志,若被读取的日志被判定为抑制日志,则其将不会记录到日志文件中,即删去这一项

字符串 s , t s,t s,t相似的定义:去除掉两者中所有数字后(相对顺序不发生改变)逐字符相等。则 s , t s,t s,t相似

为了简化题意,使得他变成一道可以做的算法题,塔子哥给出了以下条件,确保你能写好你的代码~

  1. 给定的输入保证后一条日志的时间戳不小于前一条。时间戳的取值范围是 [ 1 , 10000 ] [1,10000] [1,10000]
  2. 日志内容长度在 1000 1000 1000以内
  3. 所有数字均为正整数。

输入描述

本用例中的日志条数(最多不超过 1000 1000 1000条)和时间戳:日志打印内容

输出描述

按时间戳输出被抑制的日志。

样例

输入

5
100:1cbbb
100:2c3a2
102:2c3a2
102:2232c
103:2232c

输出

102:2c3a2
103:2232c

思路

对于这道题,我们首先会想到对于目前正在枚举的日志 i i i,我们判断它为抑制日志主要是通过将它和前面所有非抑制日志进行对比,如果和前面的非抑制日志发生冲突,那么日志 i i i也会被判定为抑制日志.

因此我们知道本题需要我们开辟一个数组去存储非抑制日志.我们记该数组为 a n s 1 ans1 ans1.当然,题目要求抑制数组,所以我们可以将抑制数组记为 a n s 2 ans2 ans2.

那么我们直接按照题目模拟即可,每次对于日志 i i i,都会从后往前遍历数组 a n s 1 ans1 ans1,判断日志是否冲突.但是我们发现时间复杂度并不过关.

一共1000个日志,每个日志最坏情况遍历整个 a n s 1 ans1 ans1数组,复杂度为 O ( 1 e 6 ) O(1e6) O(1e6),然后日志比较复杂度为 O ( 1 e 3 ) O(1e3) O(1e3),因此总时间复杂度为 O ( 1 e 9 ) O(1e9) O(1e9),可能导致超时.因此,我们可以尝试优化字符串比较.这里使用字符串哈希去优化.可以将字符串比较复杂度压缩为 O ( 1 ) O(1) O(1).最后复杂度为 O ( 1 e 6 ) O(1e6) O(1e6).

题外学习之字符串哈希:

首先我们发现对比长度为 n n n的字符串相同与否的时间复杂度为 O ( n ) O(n) O(n),类似于这道题,该复杂度添加进总体时间复杂度会导致超时.因此为了实现比较复杂度的优化,引入字符串哈希.字符串哈希主要通过将一个字符串压缩为一个整数,以后相同字符串的哈希值肯定相同,不同字符串的哈希值极大概率(概率很大,大到可以认为100%)不同.因此,我们使用整数比较后,将比较时间复杂度压缩到 O ( 1 ) O(1) O(1).当然很明显,虽然比较复杂度低了,但是由于我们需要存储字符串的哈希值,所以空间复杂度会增加,与此同时,我们求得哈希值也需要 O ( n ) O(n) O(n)的时间复杂度(可以预处理).

具体压缩方法和字符串转整数比较类似:比如字符串"12345679"和字符串"12421421",我们如果进行字符串比较需要比较8次,然而我们转为整数12345679和整数12421421后,只需要比较1次即可.

具体学习网站:https://oi-wiki.org/string/hash/

类似题目推荐

代码

CPP

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
vector<string> split(const string& s, char delimiter) {//分割字符串代码
    vector<string> tokens;
    string token;
    size_t start = 0;
    size_t end = s.find(delimiter, start);
    while (end != string::npos) {
        token = s.substr(start, end - start);
        tokens.push_back(token);
        start = end + 1;
        end = s.find(delimiter, start);
    }
    token = s.substr(start, end);
    tokens.push_back(token);
    return tokens;
}

int sift(const vector<vector<int>>& arr, int id1, int id2) {
    const vector<int>& ls1 = arr[id1];
    const vector<int>& ls2 = arr[id2];
    if (ls1[0] - ls2[0] < 10 && ls1[1] == ls2[1]) {//如果小于10ms且相同
        return 1;
    }
    else if (ls1[0] - ls2[0] < 100 && ls1[2] == ls2[2]) {{//如果小于100ms且相似
        return 2;
    }
    else if (ls1[0] - ls2[0] >= 100) {{//如果大于等于100ms
        return 0;
    }
    else {//其他情况
        return -1;
    }
}

int main() {
    int n;
    std::cin >> n;

    std::vector<vector<int>> arr;
    std::vector<string> str;
    const int seed = 133;//哈希种子
    const int hs = 1e7 + 7;//模
    string line;
    for (int i = 0; i < n; i++) {
        cin >> line;
        str.push_back(line);
        vector<string> parts = split(line, ':');
        int a = stoi(parts[0]);//a是时间
        int b = 0;//b是冒号后的字符串哈希
        int c = 0;//c是冒号后的去除数字的字符串哈希
        const string& ls = parts[1];
        for (char ch : ls) {
            if ('a' <= ch && ch <= 'z') {
                c = (c * seed + ch) % hs;
            }
            b = (b * seed + ch) % hs;
        }
        arr.push_back({ a, b, c });//将关键信息存入arr
    }

    vector<int> ans1;//存储非抑制日志
    vector<int> ans2;//存储抑制日志
    for (int i = 0; i < n; i++) {
        bool f = true;
        int cnt = 0;
        for (int j = ans1.size() - 1; j >= 0; j--) {//对于日志i,我们需要从后往前找非抑制日志,查看是否冲突
            int tar = sift(arr, i, ans1[j]);
            if (tar == 1 || (tar == 2 && cnt == 8)) {//如果冲突
                f = false;
                break;
            }
            else if (tar == 2) {//不冲突但是相似
                cnt++;
            }
            else if (tar == 0) {//和前面的日志时间相差超过100ms,就不需要再枚举
                break;
            }
        }
        if (f) {
            ans1.push_back(i);
        }
        else {
            ans2.push_back(i);
        }
    }

    for (int i : ans2) {
        cout << str[i] << std::endl;
    }

    return 0;
}

python

arr = []
def sift(id1, id2):
    ls1 = arr[id1]
    ls2 = arr[id2]
    if ls1[0] - ls2[0] < 10 and ls1[1] == ls2[1]://如果小于10ms且相同
        return 1
    elif ls1[0] - ls2[0] < 100 and ls1[2] == ls2[2]://如果小于100ms且相似
        return 2
    elif ls1[0] - ls2[0] >= 100://如果大于等于100ms
        return 0
    else://其他情况
        return -1

n = int(input())
str = []
seed = 133//哈希种子
hs = int(1e7 + 7)//for i in range(n):
    str.append(input().split(":"))
    a = int(str[-1][0])//a是时间
    b = 0//b是冒号后的字符串哈希
    c = 0//c是冒号后的去除数字的字符串哈希
    ls = list(str[-1][1])
    for j in range(len(ls)):
        if 'a' <= ls[j] <= 'z':
            c = (c * seed + ord(ls[j])) % hs
        b = (b * seed + ord(ls[j])) % hs
    arr.append([a,b,c])//将关键信息存入arr

    
ans1 = []//存储非抑制日志
ans2 = []//存储抑制日志
for i in range(n):
    f = True
    cnt = 0
    for j in range(len(ans1) - 1, -1, -1)://对于日志i,我们需要从后往前找非抑制日志,查看是否冲突
        tar = sift(i, ans1[j])
        if tar == 1 or (tar == 2 and cnt == 8)://如果冲突
            f = False
            break
        elif tar == 2://不冲突但是相似
            cnt += 1
        elif tar == 0://和前面的日志时间相差超过100ms,就不需要再枚举
            break
    if f:
        ans1.append(i)
    else:
        ans2.append(i)
tt = 0
for i in ans2:
    print(":".join(str[i]))


Java

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

class Main {
    static List<String> split(String s, char delimiter) {//分割字符串代码
        List<String> tokens = new ArrayList<>();
        StringBuilder token = new StringBuilder();
        int start = 0;
        int end = s.indexOf(delimiter, start);
        while (end != -1) {
            token.append(s, start, end);
            tokens.add(token.toString());
            token.setLength(0);
            start = end + 1;
            end = s.indexOf(delimiter, start);
        }
        token.append(s, start, s.length());
        tokens.add(token.toString());
        return tokens;
    }

    static int sift(List<List<Integer>> arr, int id1, int id2) {
        List<Integer> ls1 = arr.get(id1);
        List<Integer> ls2 = arr.get(id2);
        if (ls1.get(0) - ls2.get(0) < 10 && ls1.get(1).equals(ls2.get(1))) {//如果小于10ms且相同
            return 1;
        } else if (ls1.get(0) - ls2.get(0) < 100 && ls1.get(2).equals(ls2.get(2))) {//如果小于100ms且相似
            return 2;
        } else if (ls1.get(0) - ls2.get(0) >= 100) {//如果大于等于100ms
            return 0;
        } else {//其他情况
            return -1;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();

        List<List<Integer>> arr = new ArrayList<>();
        List<String> str = new ArrayList<>();
        final int seed = 133;
        final int hs = (int) (1e7 + 7);
        for (int i = 0; i < n; i++) {
            String line = sc.nextLine();
            str.add(line);
            List<String> parts = split(line, ':');
            int a = Integer.parseInt(parts.get(0));//a是时间
            int b = 0;//b是冒号后的字符串哈希
            int c = 0;//c是冒号后的去除数字的字符串哈希
            String ls = parts.get(1);
            for (char ch : ls.toCharArray()) {
                if (ch >= 'a' && ch <= 'z') {
                    c = (c * seed + ch) % hs;
                }
                b = (b * seed + ch) % hs;
            }
            List<Integer> sublist = new ArrayList<>();
            sublist.add(a);
            sublist.add(b);
            sublist.add(c);
            arr.add(sublist);//将关键信息存入arr
        }

        List<Integer> ans1 = new ArrayList<>();//存储非抑制日志
        List<Integer> ans2 = new ArrayList<>();//存储抑制日志
        for (int i = 0; i < n; i++) {
            boolean f = true;
            int cnt = 0;
            for (int j = ans1.size() - 1; j >= 0; j--) {//对于日志i,我们需要从后往前找非抑制日志,查看是否冲突
                int tar = sift(arr, i, ans1.get(j));
                if (tar == 1 || (tar == 2 && cnt == 8)) {//如果冲突
                    f = false;
                    break;
                } else if (tar == 2) {//不冲突但是相似
                    cnt++;
                } else if (tar == 0) {//和前面的日志时间相差超过100ms,就不需要再枚举
                    break;
                }
            }
            if (f) {
                ans1.add(i);
            } else {
                ans2.add(i);
            }
        }

        for (int i : ans2) {
            System.out.println(str.get(i));
        }
    }
}

Go

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)


func split(s string, delimiter rune) []string {//分割字符串代码
	tokens := make([]string, 0)
	start := 0
	end := strings.IndexRune(s, delimiter)
    token1 := s[start:end]
    tokens = append(tokens, token1)
	token2 := s[end + 1:]
	tokens = append(tokens, token2)
	return tokens
}

func sift(arr [][]int, id1, id2 int) int {
	ls1 := arr[id1]
	ls2 := arr[id2]
	if ls1[0]-ls2[0] < 10 && ls1[1] == ls2[1] {//如果小于10ms且相同
		return 1
	} else if ls1[0]-ls2[0] < 100 && ls1[2] == ls2[2] {//如果小于100ms且相似
		return 2
	} else if ls1[0]-ls2[0] >= 100 {//如果大于等于100ms
		return 0
	} else {//其他情况
		return -1
	}
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	n, _ := strconv.Atoi(scanner.Text())

	arr := make([][]int, n)
	str := make([]string, n)
	seed := 133
	hs := int(1e7 + 7)
	for i := 0; i < n; i++ {
		scanner.Scan()
		line := scanner.Text()
		str[i] = line
		parts := split(line, ':')
		a, _ := strconv.Atoi(parts[0])//a是时间
		b := 0//b是冒号后的字符串哈希
		c := 0//c是冒号后的去除数字的字符串哈希
		ls := parts[1]
		for _, ch := range ls {
			if ch >= 'a' && ch <= 'z' {
				c = (c*seed + int(ch)) % hs
			}
			b = (b*seed + int(ch)) % hs
		}
		arr[i] = []int{a, b, c}//将关键信息存入arr
	}

	ans1 := make([]int, 0)//存储非抑制日志
	ans2 := make([]int, 0)//存储抑制日志
	for i := 0; i < n; i++ {//对于日志i,我们需要从后往前找非抑制日志,查看是否冲突
		f := true
		cnt := 0
		for j := len(ans1) - 1; j >= 0; j-- {
			tar := sift(arr, i, ans1[j])
			if tar == 1 || (tar == 2 && cnt == 8) {//如果冲突
				f = false
				break
			} else if tar == 2 {//不冲突但是相似
				cnt++
			} else if tar == 0 {//和前面的日志时间相差超过100ms,就不需要再枚举
				break
			}
		}
		if f {
			ans1 = append(ans1, i)
		} else {
			ans2 = append(ans2, i)
		}
	}

	for _, i := range ans2 {
		fmt.Println(str[i])
	}
}

Js

const readline = require('readline');

function sift(arr, id1, id2) {
    const ls1 = arr[id1];
    const ls2 = arr[id2];
    if (ls1[0] - ls2[0] < 10 && ls1[1] === ls2[1]) {//如果小于10ms且相同
        return 1;
    } else if (ls1[0] - ls2[0] < 100 && ls1[2] === ls2[2]) {//如果小于100ms且相似
        return 2;
    } else if (ls1[0] - ls2[0] >= 100) {//如果大于等于100ms
        return 0;
    } else {//其他情况
        return -1;
    }
}

function main() {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    let n;
    const arr = [];
    const str = [];
    const seed = 133;
    const hs = 1e7 + 7;

    rl.question('', (answer) => {
        n = parseInt(answer);
        readLines();
    });

    function readLines() {
        rl.question('', (line) => {
            str.push(line);
            const parts = line.split(':');
            const a = parseInt(parts[0]);//a是时间
            let b = 0;//b是冒号后的字符串哈希
            let c = 0;//c是冒号后的去除数字的字符串哈希
            const ls = parts[1];
            for (let j = 0; j < ls.length; j++) {
                const ch = ls[j];
                if ('a' <= ch && ch <= 'z') {
                    c = (c * seed + ch.charCodeAt(0)) % hs;
                }
                b = (b * seed + ch.charCodeAt(0)) % hs;
            }
            arr.push([a, b, c]);

            if (str.length === n) {
                processInput();
            } else {
                readLines();
            }
        });
    }

    function processInput() {
        const ans1 = [];//存储非抑制日志
        const ans2 = [];//存储抑制日志
        for (let i = 0; i < n; i++) {//对于日志i,我们需要从后往前找非抑制日志,查看是否冲突
            let f = true;
            let cnt = 0;
            for (let j = ans1.length - 1; j >= 0; j--) {
                const tar = sift(arr, i, ans1[j]);
                if (tar === 1 || (tar === 2 && cnt === 8)) {//如果冲突
                    f = false;
                    break;
                } else if (tar === 2) {//不冲突但是相似
                    cnt++;
                } else if (tar === 0) {//和前面的日志时间相差超过100ms,就不需要再枚举
                    break;
                }
            }
            if (f) {
                ans1.push(i);
            } else {
                ans2.push(i);
            }
        }

        for (let i = 0; i < ans2.length; i++) {
            console.log(str[ans2[i]]);
        }

        rl.close();
    }

}

main();

题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值