2 2
10
mina hi
jun hi
mina HI
jun yo
mina hi
mina Hi
mina hi
jun yo
ab c
a bc
stdout
mina hi
jun hi
jun yo
mina hi
ab c
a bc
3 1
mina
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
这道题要问两次“在不在列表里”:在封禁列表里找一个名字,在屏幕列表里找(名字,单词)这一对。 找一个字符串是 Python name in blocked,JS/TS blocked.includes(name),Java blocked.contains(name),C# blocked.Contains(name),C++ std::find(blocked.begin(), blocked.end(), name) != blocked.end(),Craykon blocked.包含(name),C 则写一个用 strcmp 逐个比较的循环。 找一对值时各语言就不一样了。 Python 元组 (name, low) in screen、对 C++ std::pair 用 std::find、C# 装元组的 List<(string, string)> 的 Contains、Java 装着 List.of(name, low) 的列表的 contains 都比较里面的值。 陷阱一:JS/TS screen.includes([name, low])、Java List<String[]> 的 contains(new String[] {name, low})、Craykon screen.包含([name, low]) 永远为假,因为内容相同也是刚创建的另一个数组或列表。 不会报错,却一条刷屏也抓不到,示例的第 3 条消息 mina HI 会照样输出。 JS/TS 用 screen.some(([a, b]) => a === name && b === low) 比较内容,Craykon 则并排保存名字列表和单词列表,逐个位置比较。 陷阱二:也可以把这一对连接成一个字符串再查找,但不加分隔符地写成 name + low,ab + c 和 a + bc 都会变成 abc,示例的最后一条消息 a bc 会被当作刷屏过滤掉。 要在中间放一个名字和单词里都不会出现的空格。 陷阱三:要不区分大小写,放入和查找时都使用转成小写的单词(Python lower(),JS/TS toLowerCase(),Java toLowerCase(Locale.ROOT),C# ToLowerInvariant(),C/C++ 逐个字符 tolower,Craykon 小写())。 不转换的话 mina HI 就不算刷屏。 输出要按收到的原样,所以还要保留原来的单词。 陷阱四:不看名字只查找单词,会像示例的第 2 条消息 jun hi 那样把别人说的同一句话也当成刷屏。 陷阱五:把被过滤的刷屏消息也放入屏幕列表,屏幕上原有的消息会被挤掉或留得太久,示例的第 5 条消息 mina hi 会被错误地过滤。 陷阱六:放入后在 >= k 时移除,屏幕列表里只剩 k − 1 条。 移除最前面的元素是 Python pop(0),JS/TS shift(),Java remove(0),C# RemoveAt(0),C++ erase(begin()),Craykon 按位置移除(0),C 则把数组逐个向前挪。 陷阱七:要先问封禁列表。 先问屏幕列表的话,已被封禁的 mina 的第 7 条消息不是被忽略而是被算作刷屏,3 1 会变成 4 0。
Pythonmain.py · 28 行
import syslines = sys.stdin.read().split("\n")k, s = map(int, lines[0].split())n = int(lines[1])screen = [] # 화면에 남은 (이름, 소문자 낱말) 쌍, 오래된 것이 앞blocked = [] # 차단된 이름, 차단된 차례대로warn = {}repeats = ignored = 0out = []for i in range(n): name, word = lines[2 + i].split() key = (name, word.lower()) if name in blocked: ignored += 1 elif key in screen: # 튜플은 값으로 비교한다 repeats += 1 warn[name] = warn.get(name, 0) + 1 if warn[name] == s: blocked.append(name) else: out.append(f"{name} {word}") screen.append(key) if len(screen) > k: screen.pop(0)out.append(f"{repeats} {ignored}")out.append(" ".join(blocked) if blocked else "-")print("\n".join(out))
JavaScriptmain.js · 28 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [k, s] = lines[0].trim().split(/\s+/).map(Number);const n = Number(lines[1]);const screen = []; // 화면에 남은 [이름, 소문자 낱말] 쌍, 오래된 것이 앞const blocked = []; // 차단된 이름, 차단된 차례대로const warn = new Map();let repeats = 0;let ignored = 0;const out = [];for (let i = 0; i < n; i++) { const [name, word] = lines[2 + i].trim().split(/\s+/); const low = word.toLowerCase(); if (blocked.includes(name)) { ignored++; } else if (screen.some(([a, b]) => a === name && b === low)) { // includes([name, low])는 새 배열이라 늘 false다. 속을 하나씩 견준다 repeats++; warn.set(name, (warn.get(name) ?? 0) + 1); if (warn.get(name) === s) blocked.push(name); } else { out.push(`${name} ${word}`); screen.push([name, low]); if (screen.length > k) screen.shift(); }}out.push(`${repeats} ${ignored}`);out.push(blocked.length > 0 ? blocked.join(" ") : "-");console.log(out.join("\n"));
TypeScriptmain.ts · 30 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const [k, s]: number[] = lines[0].trim().split(/\s+/).map(Number);const n: number = Number(lines[1]);const screen: [string, string][] = []; // 화면에 남은 [이름, 소문자 낱말] 쌍, 오래된 것이 앞const blocked: string[] = []; // 차단된 이름, 차단된 차례대로const warn = new Map<string, number>();let repeats = 0;let ignored = 0;const out: string[] = [];for (let i = 0; i < n; i++) { const [name, word]: string[] = lines[2 + i].trim().split(/\s+/); const low: string = word.toLowerCase(); if (blocked.includes(name)) { ignored++; } else if (screen.some(([a, b]) => a === name && b === low)) { // includes([name, low])는 새 배열이라 늘 false다. 속을 하나씩 견준다 repeats++; const count: number = (warn.get(name) ?? 0) + 1; warn.set(name, count); if (count === s) blocked.push(name); } else { out.push(`${name} ${word}`); screen.push([name, low]); if (screen.length > k) screen.shift(); }}out.push(`${repeats} ${ignored}`);out.push(blocked.length > 0 ? blocked.join(" ") : "-");console.log(out.join("\n"));
JavaMain.java · 44 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Locale;import java.util.Map;public class Main { public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String[] first = in.readLine().trim().split(" "); int k = Integer.parseInt(first[0]); int s = Integer.parseInt(first[1]); int n = Integer.parseInt(in.readLine().trim()); // List.of(이름, 낱말)은 equals가 속을 견주므로 contains로 찾을 수 있다(String[]는 안 된다) List<List<String>> screen = new ArrayList<>(); List<String> blocked = new ArrayList<>(); Map<String, Integer> warn = new HashMap<>(); int repeats = 0; int ignored = 0; StringBuilder out = new StringBuilder(); for (int i = 0; i < n; i++) { String[] parts = in.readLine().trim().split(" "); String name = parts[0]; String word = parts[1]; List<String> key = List.of(name, word.toLowerCase(Locale.ROOT)); if (blocked.contains(name)) { ignored++; } else if (screen.contains(key)) { repeats++; int count = warn.merge(name, 1, Integer::sum); if (count == s) blocked.add(name); } else { out.append(name).append(' ').append(word).append('\n'); screen.add(key); if (screen.size() > k) screen.remove(0); } } out.append(repeats).append(' ').append(ignored).append('\n'); out.append(blocked.isEmpty() ? "-" : String.join(" ", blocked)).append('\n'); System.out.print(out); }}
C#Main.cs · 45 行
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { string[] first = Console.ReadLine().Trim().Split(' '); int k = int.Parse(first[0]); int s = int.Parse(first[1]); int n = int.Parse(Console.ReadLine()); // 튜플 (string, string)은 값으로 비교하므로 Contains로 찾을 수 있다(string[]은 안 된다) var screen = new List<(string, string)>(); var blocked = new List<string>(); var warn = new Dictionary<string, int>(); int repeats = 0, ignored = 0; var sb = new StringBuilder(); for (int i = 0; i < n; i++) { string[] parts = Console.ReadLine().Trim().Split(' '); string name = parts[0], word = parts[1]; var key = (name, word.ToLowerInvariant()); if (blocked.Contains(name)) { ignored++; } else if (screen.Contains(key)) { repeats++; warn[name] = warn.GetValueOrDefault(name, 0) + 1; if (warn[name] == s) blocked.Add(name); } else { sb.Append(name).Append(' ').Append(word).Append('\n'); screen.Add(key); if (screen.Count > k) screen.RemoveAt(0); } } sb.Append(repeats).Append(' ').Append(ignored).Append('\n'); sb.Append(blocked.Count > 0 ? string.Join(" ", blocked) : "-").Append('\n'); Console.Write(sb); }}
C++main.cpp · 42 行
#include <algorithm>#include <cctype>#include <iostream>#include <map>#include <string>#include <utility>#include <vector>using namespace std;int main() { int k, s, n; cin >> k >> s >> n; vector<pair<string, string>> screen; // 화면에 남은 (이름, 소문자 낱말), 오래된 것이 앞 vector<string> blocked; // 차단된 이름, 차단된 차례대로 map<string, int> warn; int repeats = 0, ignored = 0; for (int i = 0; i < n; i++) { string name, word; cin >> name >> word; string low = word; for (char &c : low) c = (char)tolower((unsigned char)c); pair<string, string> key{name, low}; if (find(blocked.begin(), blocked.end(), name) != blocked.end()) { ignored++; } else if (find(screen.begin(), screen.end(), key) != screen.end()) { // pair는 값으로 비교한다 repeats++; if (++warn[name] == s) blocked.push_back(name); } else { cout << name << ' ' << word << '\n'; screen.push_back(key); if ((int)screen.size() > k) screen.erase(screen.begin()); } } cout << repeats << ' ' << ignored << '\n'; if (blocked.empty()) { cout << "-\n"; } else { for (size_t i = 0; i < blocked.size(); i++) cout << (i ? " " : "") << blocked[i]; cout << '\n'; } return 0;}
Cmain.c · 68 行
#include <ctype.h>#include <stdio.h>#include <string.h>#define MAXK 50#define MAXN 2000static char scr_name[MAXK][9], scr_word[MAXK][9]; /* 화면 목록, 오래된 것이 앞 */static char people[MAXN][9]; /* 도배한 적이 있는 이름 */static int warn[MAXN];static char blocked[MAXN][9];/* C에는 목록 찾기가 없다. strcmp로 하나씩 견준다 */static int find_name(char list[][9], int count, const char *name) { for (int i = 0; i < count; i++) if (strcmp(list[i], name) == 0) return i; return -1;}int main(void) { int k, s, n; if (scanf("%d %d %d", &k, &s, &n) != 3) return 1; int screen = 0, npeople = 0, nblocked = 0, repeats = 0, ignored = 0; for (int i = 0; i < n; i++) { char name[9], word[9], low[9]; if (scanf("%8s %8s", name, word) != 2) return 1; size_t len = strlen(word); for (size_t j = 0; j <= len; j++) low[j] = (char)tolower((unsigned char)word[j]); if (find_name(blocked, nblocked, name) >= 0) { ignored++; continue; } int on_screen = 0; for (int j = 0; j < screen; j++) if (strcmp(scr_name[j], name) == 0 && strcmp(scr_word[j], low) == 0) on_screen = 1; if (on_screen) { repeats++; int p = find_name(people, npeople, name); if (p < 0) { p = npeople++; strcpy(people[p], name); warn[p] = 0; } if (++warn[p] == s) strcpy(blocked[nblocked++], name); } else { printf("%s %s\n", name, word); if (screen == k) { /* 꽉 찼으면 가장 오래된 것을 빼고 한 칸씩 당긴다 */ for (int j = 1; j < screen; j++) { strcpy(scr_name[j - 1], scr_name[j]); strcpy(scr_word[j - 1], scr_word[j]); } screen--; } strcpy(scr_name[screen], name); strcpy(scr_word[screen], low); screen++; } } printf("%d %d\n", repeats, ignored); if (nblocked == 0) { printf("-\n"); } else { for (int i = 0; i < nblocked; i++) printf("%s%s", i ? " " : "", blocked[i]); printf("\n"); } return 0;}