From cba7a781c760b17a05143196577c2ff4afde79ba Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sat, 20 Sep 2025 15:00:28 +0000
Subject: [PATCH 01/46] feat: add 2018fall/lab4-1159
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_4/lab_4_1159/README.md | 52 +++++++++
2018fall/lab_4/lab_4_1159/pom.xml | 22 ++++
.../lab_4/lab_4_1159/resources/01.data.in | 7 ++
.../lab_4/lab_4_1159/resources/01.data.out | 6 +
2018fall/lab_4/lab_4_1159/src/Main.java | 106 ++++++++++++++++++
2018fall/lab_4/lab_4_1159/test/MainTest.java | 45 ++++++++
2018fall/lab_4/pom.xml | 31 +++++
2018fall/pom.xml | 1 +
8 files changed, 270 insertions(+)
create mode 100644 2018fall/lab_4/lab_4_1159/README.md
create mode 100644 2018fall/lab_4/lab_4_1159/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1159/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1159/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1159/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1159/test/MainTest.java
create mode 100644 2018fall/lab_4/pom.xml
diff --git a/2018fall/lab_4/lab_4_1159/README.md b/2018fall/lab_4/lab_4_1159/README.md
new file mode 100644
index 0000000..b8bf77e
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1159/README.md
@@ -0,0 +1,52 @@
+## Description from website
+
+One day lanran finds that his name can be found in both string “lanran2001” and “20lanran01”.
+
+Now lanran gives you T(T<=1000) strings (string.length() <= 100).
+
+For each string, you can remove any substring of them to make it become the string “lanran”.
+
+If you can do this, print a “YES” in a line, otherwise print “NO”.
+
+> string.length() <= 100)。对于每个字符串,你可以删除其中的任意子串,使其变成字符串 “lanran”。如果可以,就打印一行 “YES”,否则打印 “NO”。
+
+### Input
+
+The first line will be an integer T(T<=1000), which is the number of given string.
+
+For each test data, there will be one line containing a string of lowercase characters(‘a’ – ‘z’) and 0-9 digits.
+
+> 第一行将是一个整数 T(T<=1000),表示给定字符串的数量。 对于每个测试数据,将有一行包含小写字母(‘a’ – ‘z’)和 0-9 数字的字符串。
+
+### Output
+
+For each given string, print the answer of lanran’s question.
+
+> 对于每个给定的字符串,打印出 lanran 问题的答案。
+
+### Sample Input
+
+``` log
+6
+lanran
+lanran2001
+20lan0r1an
+lanan
+nanla
+larann
+```
+
+### Sample Output
+
+``` log
+YES
+YES
+YES
+NO
+NO
+NO
+```
+
+## 解答
+
+没什么好分析的, 输入, 输出都没有坑, 单纯检查输入的字符串内是否含有顺序的那六个字母, 哪怕是硬写if-else都能过.
diff --git a/2018fall/lab_4/lab_4_1159/pom.xml b/2018fall/lab_4/lab_4_1159/pom.xml
new file mode 100644
index 0000000..d07f01d
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1159/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab3
+ lab_4_1159
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1159/resources/01.data.in b/2018fall/lab_4/lab_4_1159/resources/01.data.in
new file mode 100644
index 0000000..dab0553
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1159/resources/01.data.in
@@ -0,0 +1,7 @@
+6
+lanran
+lanran2001
+20lan0r1an
+lanan
+nanla
+larann
diff --git a/2018fall/lab_4/lab_4_1159/resources/01.data.out b/2018fall/lab_4/lab_4_1159/resources/01.data.out
new file mode 100644
index 0000000..336ab6b
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1159/resources/01.data.out
@@ -0,0 +1,6 @@
+YES
+YES
+YES
+NO
+NO
+NO
diff --git a/2018fall/lab_4/lab_4_1159/src/Main.java b/2018fall/lab_4/lab_4_1159/src/Main.java
new file mode 100644
index 0000000..9e3b00b
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1159/src/Main.java
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static List reader() {
+ final var input = new Reader();
+ final int testcases = input.nextInt();
+ assert ((testcases >= 0) && (testcases <= 1000)) : "T must be between 0 and 1000";
+ final List cases = new ArrayList<>(testcases);
+ for (int i = 0; i < testcases; i++) {
+ final String s = input.next();
+ assert (s.length() <= 100) : "string.length() must be <= 100";
+ assert (s.matches("[a-z0-9]+")) : "String must contain only lowercase letters and digits";
+ cases.add(s);
+ }
+ return cases;
+ }
+
+ private static final String target = "lanran";
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (String s : inputs) {
+ int i = 0; // pointer for string s
+ int j = 0; // pointer for target
+ while (i < s.length() && j < target.length()) {
+ if (s.charAt(i) == target.charAt(j)) {
+ j++;
+ }
+ i++;
+ }
+ if (j == target.length()) {
+ results.add("YES");
+ } else {
+ results.add("NO");
+ }
+ }
+ return results;
+ }
+
+ public static void main(String[] args) {
+ final var datas = reader();
+ final var result = cal(datas);
+ output(result);
+ }
+
+ public static void output(List decides) {
+ for (var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ long nextLong() {
+ return Long.parseLong(next());
+ }
+
+ double nextDouble() {
+ return Double.parseDouble(next());
+ }
+
+ String nextLine() {
+ String str = "";
+ try {
+ str = br.readLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return str;
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1159/test/MainTest.java b/2018fall/lab_4/lab_4_1159/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1159/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
new file mode 100644
index 0000000..f184616
--- /dev/null
+++ b/2018fall/lab_4/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template
+ 2018fall
+ ${revision}
+ ./../pom.xml
+
+
+ pom
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+ lab_4_1159
+
+
+
+ nanoseeds.algorithm-template
+ test_include_package
+ ${revision}
+ test
+
+
+
+
diff --git a/2018fall/pom.xml b/2018fall/pom.xml
index e30deab..2427ebb 100644
--- a/2018fall/pom.xml
+++ b/2018fall/pom.xml
@@ -19,5 +19,6 @@
lab_example
lab_2
lab_3
+ lab_4
From 15a8151d54052a02e64df39b0f7d13fd9acfd30f Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sat, 20 Sep 2025 15:13:07 +0000
Subject: [PATCH 02/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?4-1039?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_4/lab_4_1039/README.md | 69 ++++++++++
2018fall/lab_4/lab_4_1039/pom.xml | 22 ++++
.../lab_4/lab_4_1039/resources/01.data.in | 15 +++
.../lab_4/lab_4_1039/resources/01.data.out | 7 ++
2018fall/lab_4/lab_4_1039/src/Main.java | 118 ++++++++++++++++++
2018fall/lab_4/lab_4_1039/test/MainTest.java | 45 +++++++
2018fall/lab_4/pom.xml | 1 +
7 files changed, 277 insertions(+)
create mode 100644 2018fall/lab_4/lab_4_1039/README.md
create mode 100644 2018fall/lab_4/lab_4_1039/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1039/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1039/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1039/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1039/test/MainTest.java
diff --git a/2018fall/lab_4/lab_4_1039/README.md b/2018fall/lab_4/lab_4_1039/README.md
new file mode 100644
index 0000000..0de9092
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1039/README.md
@@ -0,0 +1,69 @@
+## Description
+
+There are n brackets. And you want to know whether they are match to each other.
+
+The brackets will only contain `{` `}` `(` `)` `[` `]`.
+
+The matching rules are the same as in Math.
+
+For example, `{{[}]}` is not matching, and `[{{}}()]` is matching.
+
+Please write a program to check whether the given brackets string is matching or not.
+
+### Input
+
+The first line will be an integer T, which is the number of test cases. (1 <= T <= 10)
+
+For each test case, the first line will be an integer n ( 1 <= n <= 30000)
+
+Then there is a line with n brackets.
+
+### Output
+
+For each test case, print `YES` if the test case is a matching case. Otherwise, print `NO`.
+
+### Sample Input
+
+```log
+7
+1
+(
+2
+()
+2
+{]
+6
+[(){}]
+4
+(])[
+8
+[[{{}}]]
+6
+[][{]]
+```
+
+### Sample Output
+
+``` log
+NO
+YES
+NO
+YES
+NO
+YES
+NO
+```
+
+## 解答
+
+这道题就是经典的括号匹配问题,属于数据结构课程的入门必刷题。
+
+解决思路非常直接:使用一个栈(在 Java 里我们用更现代的 `Deque` 实现)。遍历输入的括号字符串:
+- 遇到开括号(`{`, `[`, `(`),就把它压进栈里。
+- 遇到闭括号(`}`, `]`, `)`),就从栈顶取出一个开括号来配对。如果栈是空的,或者取出的开括号不匹配,那这个字符串肯定就不可行了。
+ - 注意不要一个字符一个字符的读, 然后读到一半就break, 这会导致后续数据乱掉.
+ - 如果不允许引用数据结构, 请先实现一个Stack, 然后用这个Stack来实现
+
+
+遍历完整个字符串后,如果栈正好是空的,说明所有括号都完美配对,输出 "YES";否则就是 "NO"。
+
diff --git a/2018fall/lab_4/lab_4_1039/pom.xml b/2018fall/lab_4/lab_4_1039/pom.xml
new file mode 100644
index 0000000..d5cb25e
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1039/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab3
+ lab_4_1039
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1039/resources/01.data.in b/2018fall/lab_4/lab_4_1039/resources/01.data.in
new file mode 100644
index 0000000..c3bf301
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1039/resources/01.data.in
@@ -0,0 +1,15 @@
+7
+1
+(
+2
+()
+2
+{]
+6
+[(){}]
+4
+(])[
+8
+[[{{}}]]
+6
+[][{]]
diff --git a/2018fall/lab_4/lab_4_1039/resources/01.data.out b/2018fall/lab_4/lab_4_1039/resources/01.data.out
new file mode 100644
index 0000000..8e8f811
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1039/resources/01.data.out
@@ -0,0 +1,7 @@
+NO
+YES
+NO
+YES
+NO
+YES
+NO
diff --git a/2018fall/lab_4/lab_4_1039/src/Main.java b/2018fall/lab_4/lab_4_1039/src/Main.java
new file mode 100644
index 0000000..b63cfaf
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1039/src/Main.java
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static List reader() {
+ final var input = new Reader();
+ final int testcases = input.nextInt();
+ assert testcases >= 1 && testcases <= 10 : "T must be between 1 and 10";
+ final List cases = new ArrayList<>(testcases);
+ for (int i = 0; i < testcases; i++) {
+ int n = input.nextInt();
+ final String s = input.next();
+ assert s.length() == n : "n should be equal to the length of the string";
+ assert n >= 1 && n <= 30000 : "n must be between 1 and 30000";
+ assert s.matches("[\\{\\}\\[\\]\\(\\)]+") : "String must contain only brackets";
+ cases.add(s);
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (String s : inputs) {
+ if (isBalanced(s)) {
+ results.add("YES");
+ } else {
+ results.add("NO");
+ }
+ }
+ return results;
+ }
+
+ private static boolean isBalanced(String s) {
+ final Deque stack = new ArrayDeque<>();
+ for (char c : s.toCharArray()) {
+ if (c == '(' || c == '{' || c == '[') {
+ stack.push(c);
+ } else {
+ if (stack.isEmpty()) {
+ return false;
+ }
+ char top = stack.pop();
+ if ((c == ')' && top != '(') || (c == '}' && top != '{') || (c == ']' && top != '[')) {
+ return false;
+ }
+ }
+ }
+ return stack.isEmpty();
+ }
+
+ public static void main(String[] args) {
+ final var datas = reader();
+ final var result = cal(datas);
+ output(result);
+ }
+
+ public static void output(List decides) {
+ for (var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ long nextLong() {
+ return Long.parseLong(next());
+ }
+
+ double nextDouble() {
+ return Double.parseDouble(next());
+ }
+
+ String nextLine() {
+ String str = "";
+ try {
+ str = br.readLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return str;
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1039/test/MainTest.java b/2018fall/lab_4/lab_4_1039/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1039/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
index f184616..350aa5b 100644
--- a/2018fall/lab_4/pom.xml
+++ b/2018fall/lab_4/pom.xml
@@ -18,6 +18,7 @@
${project.groupId}.${project.artifactId}
lab_4_1159
+ lab_4_1039
From 3d7d1be3f9ddd31505ee081ea003705699f60d39 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sat, 20 Sep 2025 15:38:43 +0000
Subject: [PATCH 03/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?4-1161?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_4/README.md | 7 +
2018fall/lab_4/lab_4_1161/README.md | 49 +++++++
2018fall/lab_4/lab_4_1161/pom.xml | 22 +++
.../lab_4/lab_4_1161/resources/01.data.in | 5 +
.../lab_4/lab_4_1161/resources/01.data.out | 2 +
2018fall/lab_4/lab_4_1161/src/Main.java | 131 ++++++++++++++++++
2018fall/lab_4/lab_4_1161/test/MainTest.java | 45 ++++++
2018fall/lab_4/pom.xml | 1 +
8 files changed, 262 insertions(+)
create mode 100644 2018fall/lab_4/README.md
create mode 100644 2018fall/lab_4/lab_4_1161/README.md
create mode 100644 2018fall/lab_4/lab_4_1161/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1161/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1161/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1161/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1161/test/MainTest.java
diff --git a/2018fall/lab_4/README.md b/2018fall/lab_4/README.md
new file mode 100644
index 0000000..2dadc47
--- /dev/null
+++ b/2018fall/lab_4/README.md
@@ -0,0 +1,7 @@
+# 2018fall-lab4
+
+## Stack And Queue
+
++ problem A: lab_4_1159
++ problem B: lab_4_1039
++ problem C: lab_4_1161
diff --git a/2018fall/lab_4/lab_4_1161/README.md b/2018fall/lab_4/lab_4_1161/README.md
new file mode 100644
index 0000000..5693155
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1161/README.md
@@ -0,0 +1,49 @@
+## Description
+
+Give you an increasing sequence A of length n, and find the number of ordered tuples (i, j, k) such that the maximum element in {A[i], A[j], A[k]} minus the minimum element in {A[i], A[j], A[k]} <= m.
+
+### Input
+
+The first line of the input is T (T <= 5), which is the number of test cases.
+
+For each test cases, the first line is n, m, the next line contains n integers which is the given sequence A. (1 <= n <= 100000 ; 1 <= m <= 1000000000) abs(A[i] <= 1000000000).
+
+### Output
+
+For each test cases, print the number of tuples satisfying requirements in one line.
+
+### Sample Input
+
+``` log
+2
+4 3
+1 2 3 4
+5 19
+1 10 20 30 50
+```
+
+### Sample Output
+
+``` log
+4
+1
+```
+
+## 解答
+
+本题要求在一个递增序列 `A` 中,找出满足 `最大值 - 最小值 <= m` 的三元组 `(i, j, k)` 的数量。
+
+由于序列 `A` 是递增的,对于任意满足 `i < j < k` 的索引,三元组 `{A[i], A[j], A[k]}` 的最小值就是 `A[i]`,最大值就是 `A[k]`。因此,问题可以转化为:找出所有满足 `i < j < k` 且 `A[k] - A[i] <= m` 的索引组合 `(i, j, k)` 的数量。
+
+如果直接使用三层循环暴力枚举 `i, j, k`,时间复杂度将达到 `O(n^3)`,在 `n` 最大为 100,000 的情况下会超时。
+
+我们可以采用更高效的双指针(或称滑动窗口) 算法,将时间复杂度优化到 `O(n)`。
+
+算法思路如下:
+1. 我们从左到右遍历数组,用一个指针 `i` 来固定三元组中最小的那个数 `A[i]`。
+2. 对于每个固定的 `i`,我们用另一个指针 `right` 从 `i` 开始向右移动,找到一个最远的位置,使得所有在 `i` 和 `right` 之间的数 `A[x]` 都满足 `A[x] - A[i] <= m`。
+3. 这样,我们就得到了一个“窗口” `[i, right-1]`。对于固定的 `i`,我们需要在这个窗口内再选择两个数 `A[j]` 和 `A[k]`。选择的范围是从 `i+1` 到 `right-1`,共有 `(right - 1) - (i + 1) + 1 = right - i - 1` 个元素。
+4. 从这 `right - i - 1` 个元素中任意选择两个,就是一个满足条件的三元组。这本质上是一个组合问题,数量为 `C(right - i - 1, 2)`。
+5. 我们将每个 `i` 对应的组合数累加起来,就是最终的答案。
+
+代码实现中,我们用一个 `for` 循环来移动左指针 `i`,用一个 `while` 循环来扩展右指针 `right`。由于 `right` 指针只增不减,整个算法的两个指针都只遍历了一遍数组,总时间复杂度为 `O(n)`。由于结果可能很大,我们使用 `BigInteger` 来存储最终的计数值。
diff --git a/2018fall/lab_4/lab_4_1161/pom.xml b/2018fall/lab_4/lab_4_1161/pom.xml
new file mode 100644
index 0000000..b4047b9
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1161/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab3
+ lab_4_1161
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1161/resources/01.data.in b/2018fall/lab_4/lab_4_1161/resources/01.data.in
new file mode 100644
index 0000000..bd716f0
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1161/resources/01.data.in
@@ -0,0 +1,5 @@
+2
+4 3
+1 2 3 4
+5 19
+1 10 20 30 50
diff --git a/2018fall/lab_4/lab_4_1161/resources/01.data.out b/2018fall/lab_4/lab_4_1161/resources/01.data.out
new file mode 100644
index 0000000..dcb4347
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1161/resources/01.data.out
@@ -0,0 +1,2 @@
+4
+1
diff --git a/2018fall/lab_4/lab_4_1161/src/Main.java b/2018fall/lab_4/lab_4_1161/src/Main.java
new file mode 100644
index 0000000..5a6156b
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1161/src/Main.java
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ public final long m;
+ public final long[] A;
+
+ public TestCase(long m, long[] A) {
+ this.m = m;
+ this.A = A;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testcases = in.nextInt();
+ assert (testcases <= 5) : "T must be <= 5";
+ final List cases = new ArrayList<>(testcases);
+ for (int t = 0; t < testcases; t++) {
+ final int n = in.nextInt();
+ assert ((n >= 1) && (n <= 100000)) : "n must be between 1 and 100000";
+ final long m = in.nextLong();
+ assert ((m >= 1) && (m <= 1000000000)) : "m must be between 1 and 10^9";
+ final long[] A = new long[n];
+ for (int i = 0; i < n; i++) {
+ A[i] = in.nextInt();
+ assert (Math.abs(A[i]) <= 1000000000) : "abs(A[i]) must be <= 10^9";
+ if (i > 0) {
+ assert (A[i] >= A[i - 1]) : "A must be an increasing sequence";
+ }
+ }
+ cases.add(new TestCase(m, A));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (var testCase : inputs) {
+ final long m = testCase.m;
+ final long[] A = testCase.A;
+ final int n = A.length;
+ var count = BigInteger.ZERO;
+ int right = 0;
+ // O(n) 双指针/滑动窗口算法
+ for (int i = 0; i < n; i++) {
+ // 对于每个 i, 找到满足条件的最远 right
+ while (right < n && A[right] - A[i] <= m) {
+ right++;
+ }
+ // 从 i+1 到 right-1 中选择两个数作为 j 和 k
+ long len = right - i;
+ if (len >= 3) {
+ // 计算组合数 C(len-1, 2)
+ BigInteger temp = BigInteger.valueOf(len - 1).multiply(BigInteger.valueOf(len - 2)).divide(BigInteger.TWO);
+ count = count.add(temp);
+ }
+ }
+ results.add(count);
+ }
+ return results;
+ }
+
+ public static void output(List decides) {
+ for (var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ final var datas = reader();
+ final var result = cal(datas);
+ output(result);
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ long nextLong() {
+ return Long.parseLong(next());
+ }
+
+ double nextDouble() {
+ return Double.parseDouble(next());
+ }
+
+ String nextLine() {
+ String str = "";
+ try {
+ str = br.readLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return str;
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1161/test/MainTest.java b/2018fall/lab_4/lab_4_1161/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1161/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
index 350aa5b..a4dfc28 100644
--- a/2018fall/lab_4/pom.xml
+++ b/2018fall/lab_4/pom.xml
@@ -19,6 +19,7 @@
lab_4_1159
lab_4_1039
+ lab_4_1161
From 0ae4b8e320c63919d705e2267bbddc8860354dbd Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sat, 20 Sep 2025 15:56:46 +0000
Subject: [PATCH 04/46] feat: add 2018fall/lab4-1162
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_4/README.md | 1 +
2018fall/lab_4/lab_4_1162/README.md | 79 ++++++++
2018fall/lab_4/lab_4_1162/pom.xml | 22 +++
.../lab_4/lab_4_1162/resources/01.data.in | 15 ++
.../lab_4/lab_4_1162/resources/01.data.out | 2 +
2018fall/lab_4/lab_4_1162/src/Main.java | 177 ++++++++++++++++++
2018fall/lab_4/lab_4_1162/test/MainTest.java | 45 +++++
2018fall/lab_4/pom.xml | 1 +
8 files changed, 342 insertions(+)
create mode 100644 2018fall/lab_4/lab_4_1162/README.md
create mode 100644 2018fall/lab_4/lab_4_1162/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1162/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1162/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1162/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1162/test/MainTest.java
diff --git a/2018fall/lab_4/README.md b/2018fall/lab_4/README.md
index 2dadc47..a2a8ddc 100644
--- a/2018fall/lab_4/README.md
+++ b/2018fall/lab_4/README.md
@@ -5,3 +5,4 @@
+ problem A: lab_4_1159
+ problem B: lab_4_1039
+ problem C: lab_4_1161
++ problem D: lab_4_1162
diff --git a/2018fall/lab_4/lab_4_1162/README.md b/2018fall/lab_4/lab_4_1162/README.md
new file mode 100644
index 0000000..4ede94d
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1162/README.md
@@ -0,0 +1,79 @@
+## Description
+
+Yee_172 is in a maze of n row and m columns.
+
+He is in position S and he wants to go to position V since he wants to find his friend Vince.
+
+Vince has told Yee_172 how to find position E by giving Yee_172 a sequence of directions: 01123321…….
+
+It confuses Yee_172 because Vince only told him that 0123 means four directions, but not specifically up, left, right, or down.
+
+(i.e. the mappings relationship between the digits and specific directions are unknown)
+
+Therefore, Yee_172 turns for your help.
+
+You need to calculate the number of mappings of digits to directions that will lead Yee_172 to Vince.
+
+### Input
+
+The first line is T, which is the number of test cases, (T <= 10);
+
+For each test case, the first line will be integer n, m, and then n lines following,
+
+each line contains m characters (‘#’ means a beautiful girl, if Yee_172 go to ‘#’, he will stop finding Vince, ‘S’ is the start point and ‘E’ is Vince point.) N, m <= 50;
+
+After the n lines, there will be one line contains 0, 1, 2, 3 only, meaning the sequence Yee have, which has a length of no more than 100.
+
+### Output
+
+For each test case, print a single integer, which the number of directions permutation that leads the Yee_172 to the Vince.
+
+### Sample Input
+
+``` log
+2
+5 6
+.....#
+S....#
+.#....
+.#....
+...E..
+333300012
+5 3
+...
+.S.
+###
+.E.
+...
+3
+```
+
+### Sample Output
+
+``` log
+1
+0
+```
+
+## 解答
+
+这道题的本质是找出数字(0, 1, 2, 3)与四个基本方向(上, 下, 左, 右)之间有多少种有效的映射关系,能够使得角色从起点 'S' 沿着给定的指令序列成功到达终点 'E'。
+
+由于数字和方向都是四个,这是一个经典的全排列问题。总共有 `4! = 24` 种可能的映射关系。考虑到数据量不大(迷宫大小不超过 50x50,指令长度不超过 100),我们可以直接暴力枚举这 24 种可能性。
+
+代码的实现思路遵循了这一逻辑,并采用了“读-处理-输出”的分离模式:
+
+1. **`reader()` 方法**: 负责读取所有输入,包括迷宫的布局和指令序列,并将每个测试用例的数据封装起来。
+
+2. **`cal()` 方法**: 这是核心处理部分。它首先通过三层循环,手动生成了 `[0, 1, 2, 3]` 的全部 24 种排列,并存储在 `judge` 数组中。每一种排列都代表一种“数字->方向”的映射。然后,它遍历这 24 种映射,对每一种都调用 `simulate()` 方法进行路径模拟。
+
+3. **`simulate()` 方法**: 该方法负责对某一种特定的映射关系进行模拟。它从 'S' 点出发,严格按照指令序列和当前映射的移动规则(例如,`p[0]` 对应向下,`p[1]` 对应向右等)来移动。在模拟过程中:
+ * 如果路径移出边界或撞到墙壁 ('#'),则该映射无效,模拟失败。
+ * 如果路径在任何一步成功到达终点 'E',则该映射有效,模拟成功并立即返回。
+ * 如果走完了所有指令仍未到达 'E',则模拟失败。
+
+4. **`output()` 方法**: 负责将 `cal()` 方法统计出的、每个测试用例的有效映射数量进行打印。
+
+综上所述,该解法通过暴力枚举所有 24 种可能性,并对每一种可能性进行路径模拟,最终统计出成功的次数,从而得到答案。
+
+> 题目内的case似乎较弱, 没有半路到达终点的情况
diff --git a/2018fall/lab_4/lab_4_1162/pom.xml b/2018fall/lab_4/lab_4_1162/pom.xml
new file mode 100644
index 0000000..447492b
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1162/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab3
+ lab_4_1162
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1162/resources/01.data.in b/2018fall/lab_4/lab_4_1162/resources/01.data.in
new file mode 100644
index 0000000..14478cf
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1162/resources/01.data.in
@@ -0,0 +1,15 @@
+2
+5 6
+.....#
+S....#
+.#....
+.#....
+...E..
+333300012
+5 3
+...
+.S.
+###
+.E.
+...
+3
diff --git a/2018fall/lab_4/lab_4_1162/resources/01.data.out b/2018fall/lab_4/lab_4_1162/resources/01.data.out
new file mode 100644
index 0000000..b261da1
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1162/resources/01.data.out
@@ -0,0 +1,2 @@
+1
+0
diff --git a/2018fall/lab_4/lab_4_1162/src/Main.java b/2018fall/lab_4/lab_4_1162/src/Main.java
new file mode 100644
index 0000000..bb0fe03
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1162/src/Main.java
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ // Using a static final class for JDK 11 compatibility
+ public static final class TestCase {
+ public final int n, m;
+ public final char[][] maze;
+ public final String instructions;
+ public final int startX, startY, endX, endY;
+
+ public TestCase(int n, int m, char[][] maze, String instructions, int startX, int startY, int endX, int endY) {
+ this.n = n;
+ this.m = m;
+ this.maze = maze;
+ this.instructions = instructions;
+ this.startX = startX;
+ this.startY = startY;
+ this.endX = endX;
+ this.endY = endY;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testcases = in.nextInt();
+ final List cases = new ArrayList<>(testcases);
+ for (int t = 0; t < testcases; t++) {
+ final int n = in.nextInt();
+ final int m = in.nextInt();
+ final char[][] maze = new char[n][m];
+ int startX = -1, startY = -1, endX = -1, endY = -1;
+ for (int i = 0; i < n; i++) {
+ final String line = in.nextLine();
+ for (int j = 0; j < m; j++) {
+ maze[i][j] = line.charAt(j);
+ if (maze[i][j] == 'S') {
+ startX = i;
+ startY = j;
+ } else if (maze[i][j] == 'E') {
+ endX = i;
+ endY = j;
+ }
+ }
+ }
+ final String instructions = in.nextLine();
+ cases.add(new TestCase(n, m, maze, instructions, startX, startY, endX, endY));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+
+ int caltimes = 0;
+ int[][] judge = new int[24][4];
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ if (i != j && j != k && k != i) {
+ judge[caltimes][0] = i;
+ judge[caltimes][1] = j;
+ judge[caltimes][2] = k;
+ judge[caltimes][3] = 6 - i - j - k;
+ caltimes++;
+ }
+ }
+ }
+ }
+ for (var testCase : inputs) {
+ int validMappings = 0;
+ for (int i = 0; i < 24; i++) {
+ if (simulate(testCase, judge[i])) {
+ validMappings++;
+ }
+ }
+ results.add(validMappings);
+ }
+ return results;
+ }
+
+ private static boolean simulate(TestCase tc, int[] p) {
+ int x = tc.startX;
+ int y = tc.startY;
+
+ for (char instruction : tc.instructions.toCharArray()) {
+ int digit = instruction - '0';
+
+ if (digit == p[0]) {
+ x++;
+ } else if (digit == p[1]) {
+ y++;
+ } else if (digit == p[2]) {
+ x--;
+ } else if (digit == p[3]) {
+ y--;
+ }
+
+ if (x < 0 || x >= tc.n || y < 0 || y >= tc.m) {
+ return false; // Out of bounds
+ }
+ if (tc.maze[x][y] == '#') {
+ return false; // Hit a wall
+ }
+ if (x == tc.endX && y == tc.endY) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void output(List decides) {
+ for (Integer decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ final var datas = reader();
+ final var result = cal(datas);
+ output(result);
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ long nextLong() {
+ return Long.parseLong(next());
+ }
+
+ double nextDouble() {
+ return Double.parseDouble(next());
+ }
+
+ String nextLine() {
+ String str = "";
+ try {
+ str = br.readLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return str;
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1162/test/MainTest.java b/2018fall/lab_4/lab_4_1162/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1162/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
index a4dfc28..3db3cb6 100644
--- a/2018fall/lab_4/pom.xml
+++ b/2018fall/lab_4/pom.xml
@@ -20,6 +20,7 @@
lab_4_1159
lab_4_1039
lab_4_1161
+ lab_4_1162
From f309dfd4688020460ca33c66ebbb99eec330b301 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sat, 20 Sep 2025 16:17:21 +0000
Subject: [PATCH 05/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?4-1164?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_4/README.md | 10 +-
2018fall/lab_4/lab_4_1164/README.md | 88 +++++++
2018fall/lab_4/lab_4_1164/pom.xml | 22 ++
.../lab_4/lab_4_1164/resources/01.data.in | 7 +
.../lab_4/lab_4_1164/resources/01.data.out | 2 +
2018fall/lab_4/lab_4_1164/src/Main.java | 222 ++++++++++++++++++
2018fall/lab_4/lab_4_1164/test/MainTest.java | 45 ++++
2018fall/lab_4/pom.xml | 1 +
8 files changed, 393 insertions(+), 4 deletions(-)
create mode 100644 2018fall/lab_4/lab_4_1164/README.md
create mode 100644 2018fall/lab_4/lab_4_1164/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1164/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1164/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1164/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1164/test/MainTest.java
diff --git a/2018fall/lab_4/README.md b/2018fall/lab_4/README.md
index a2a8ddc..4578969 100644
--- a/2018fall/lab_4/README.md
+++ b/2018fall/lab_4/README.md
@@ -2,7 +2,9 @@
## Stack And Queue
-+ problem A: lab_4_1159
-+ problem B: lab_4_1039
-+ problem C: lab_4_1161
-+ problem D: lab_4_1162
++ [x] problem A: lab_4_1159
++ [x] problem B: lab_4_1039
++ [x] problem C: lab_4_1161
++ [x] problem D: lab_4_1162
++ [ ] problem E: lab_4_1163
++ [x] problem F: lab_4_1164
diff --git a/2018fall/lab_4/lab_4_1164/README.md b/2018fall/lab_4/lab_4_1164/README.md
new file mode 100644
index 0000000..fd5311b
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1164/README.md
@@ -0,0 +1,88 @@
+## Description
+
+One day, Wavator is taking his Linear algebra course. He hates calculating the expression of matrix so he wants to develop a calculator to help him.
+
+But, he got 59 in last years’ DSAA course, so he turns you for help.
+
+n square matrices of size m are given, and we define an operation like “(1+2)*1” which means the matrix 1 plus matrix 2 and then multiplies matrix 1.
+
+Wavator only wants to calculate “+” and “-” and “*”, so he denotes that “+” means A + B = C where C[i][j] = A[i][j] + B[i][j] .The rule of “-” is similar with "+".
+
+Notice that in matrix multiplication, a*b and b*a is not the same.
+
+Since the number may be too large during the calculation process, in each step you should mod 1000000007.
+
+### Input
+
+The first line contains an integer T, meaning there will be T (T<=10) test cases.
+
+For each test cases, the first line is n and m (n <= 10 and m <= 50), then there will be n parts, each part is a m * m
+
+matrix a, 0<=a[i][j] <= 10000, matrix's are numbered from 1 to n, then there will be a string s, the length of s is not
+
+greater than 50, and it is valid. (contains only 1~n numbers (index of matrix's) and “+”, “-”, “*” only) .
+
+### Output
+
+For each test case, print m lines, each line should contain m integers, meaning the value of the final matrix at this line and this column.
+
+### Sample Input
+
+``` log
+1
+2 2
+1 2
+2 1
+2 2
+3 3
+(1+2)*1
+```
+
+### Sample Output
+
+``` log
+11 10
+13 14
+```
+
+### HINT
+
+Codes of mod in c++ lang. similar using java.
+
+``` cpp
+const int MOD = 1000000007;
+
+
+inline int add(int x, int y) { return (x + y) % MOD; }
+
+
+inline int sub(int x, int y) { return (x - y + MOD) % MOD; }
+
+
+inline int mul(int x, int y) { return static_cast((long long) x * y % MOD); }
+```
+
+## 解答
+
+本题要求实现一个矩阵表达式计算器,能够处理包含 `+`、`-`、`*` 和括号的矩阵运算。
+
+这是一个经典的中缀表达式求值问题。解决此类问题的标准方法是使用双栈算法(一个操作数栈,一个操作符栈),这正是本代码 `Main.java` 所采用的核心思路。
+
+算法流程如下:
+
+1. 定义数据结构:
+ * 创建一个 `Matrix` 类,用于存储矩阵数据并实现加、减、乘三种运算。所有运算的每一步都严格按照题目要求对 `1000000007` 取模。
+
+2. 双栈求值 (`evaluateExpression` 方法):
+ * 准备两个栈:`values` 用于存放矩阵(操作数),`ops` 用于存放运算符。
+ * 遍历表达式字符串:
+ * 遇到数字,则解析出完整的矩阵编号,从输入中获取对应矩阵,压入 `values` 栈。
+ * 遇到左括号 `(`,直接压入 `ops` 栈。
+ * 遇到右括号 `)`,则不断从 `ops` 栈中弹出运算符,从 `values` 栈中弹出两个矩阵进行计算,直到遇到左括号为止。
+ * 遇到运算符 (`+`, `-`, `*`),则与 `ops` 栈顶的运算符比较优先级。如果当前运算符优先级较低或相等,就先计算栈顶的运算,再将当前运算符压栈。`*` 的优先级高于 `+` 和 `-`。
+
+3. 收尾计算:
+ * 遍历完整个表达式后,`ops` 栈中可能还有剩余的运算符,按顺序全部计算完。
+ * 最终,`values` 栈中仅剩的一个矩阵就是整个表达式的结果。
+
+整个程序将此算法封装在 `cal` 方法中,并遵循了 `reader` -> `cal` -> `output` 的清晰分离结构,使得代码易于理解和维护。
diff --git a/2018fall/lab_4/lab_4_1164/pom.xml b/2018fall/lab_4/lab_4_1164/pom.xml
new file mode 100644
index 0000000..acde97a
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1164/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab3
+ lab_4_1164
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1164/resources/01.data.in b/2018fall/lab_4/lab_4_1164/resources/01.data.in
new file mode 100644
index 0000000..fd564ea
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1164/resources/01.data.in
@@ -0,0 +1,7 @@
+1
+2 2
+1 2
+2 1
+2 2
+3 3
+(1+2)*1
diff --git a/2018fall/lab_4/lab_4_1164/resources/01.data.out b/2018fall/lab_4/lab_4_1164/resources/01.data.out
new file mode 100644
index 0000000..7776f2c
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1164/resources/01.data.out
@@ -0,0 +1,2 @@
+11 10
+13 14
diff --git a/2018fall/lab_4/lab_4_1164/src/Main.java b/2018fall/lab_4/lab_4_1164/src/Main.java
new file mode 100644
index 0000000..e21f3f1
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1164/src/Main.java
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ private static final int MOD = 1000000007;
+
+ // Helper class for Matrix operations
+ public static final class Matrix {
+ final int[][] data;
+ final int m;
+
+ Matrix(int m) {
+ this.m = m;
+ this.data = new int[m][m];
+ }
+
+ static Matrix add(Matrix a, Matrix b) {
+ int m = a.m;
+ Matrix result = new Matrix(m);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < m; j++) {
+ result.data[i][j] = (a.data[i][j] + b.data[i][j]) % MOD;
+ }
+ }
+ return result;
+ }
+
+ static Matrix subtract(Matrix a, Matrix b) {
+ int m = a.m;
+ Matrix result = new Matrix(m);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < m; j++) {
+ result.data[i][j] = (a.data[i][j] - b.data[i][j] + MOD) % MOD;
+ }
+ }
+ return result;
+ }
+
+ static Matrix multiply(Matrix a, Matrix b) {
+ int m = a.m;
+ Matrix result = new Matrix(m);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < m; j++) {
+ long sum = 0;
+ for (int k = 0; k < m; k++) {
+ sum = (sum + (long) a.data[i][k] * b.data[k][j]) % MOD;
+ }
+ result.data[i][j] = (int) sum;
+ }
+ }
+ return result;
+ }
+ }
+
+ // Using a static final class for JDK 11 compatibility
+ public static final class TestCase {
+ final List matrices;
+ final String expression;
+
+ public TestCase(List matrices, String expression) {
+ this.matrices = matrices;
+ this.expression = expression;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testcases = Integer.parseInt(in.nextLine());
+ final List cases = new ArrayList<>(testcases);
+ for (int t = 0; t < testcases; t++) {
+ final String[] nm = in.nextLine().split(" ");
+ final int n = Integer.parseInt(nm[0]);
+ final int m = Integer.parseInt(nm[1]);
+ final List matrices = new ArrayList<>(n);
+ for (int k = 0; k < n; k++) {
+ final var matrix = new Matrix(m);
+ for (int i = 0; i < m; i++) {
+ final var row = in.nextLine().split(" ");
+ for (int j = 0; j < m; j++) {
+ matrix.data[i][j] = Integer.parseInt(row[j]);
+ }
+ }
+ matrices.add(matrix);
+ }
+ final var expression = in.nextLine();
+ cases.add(new TestCase(matrices, expression));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ results.add(evaluateExpression(tc.expression, tc.matrices));
+ }
+ return results;
+ }
+
+ private static Matrix evaluateExpression(String expression, List matrices) {
+ final Deque values = new ArrayDeque<>();
+ final Deque ops = new ArrayDeque<>();
+
+ for (int i = 0; i < expression.length(); i++) {
+ char c = expression.charAt(i);
+ if (Character.isDigit(c)) {
+ final var sb = new StringBuilder();
+ while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
+ sb.append(expression.charAt(i++));
+ }
+ i--;
+ int matrixIndex = Integer.parseInt(sb.toString()) - 1;
+ values.push(matrices.get(matrixIndex));
+ } else if (c == '(') {
+ ops.push(c);
+ } else if (c == ')') {
+ while (ops.peek() != '(') {
+ values.push(applyOp(ops.pop(), values.pop(), values.pop()));
+ }
+ ops.pop();
+ } else if (c == '+' || c == '-' || c == '*') {
+ while (!ops.isEmpty() && hasPrecedence(c, ops.peek())) {
+ values.push(applyOp(ops.pop(), values.pop(), values.pop()));
+ }
+ ops.push(c);
+ }
+ }
+
+ while (!ops.isEmpty()) {
+ values.push(applyOp(ops.pop(), values.pop(), values.pop()));
+ }
+ return values.pop();
+ }
+
+ private static boolean hasPrecedence(char op1, char op2) {
+ if (op2 == '(' || op2 == ')') {
+ return false;
+ }
+ return (op1 != '*') || (op2 != '+' && op2 != '-');
+ }
+
+ private static Matrix applyOp(char op, Matrix b, Matrix a) {
+ switch (op) {
+ case '+':
+ return Matrix.add(a, b);
+ case '-':
+ return Matrix.subtract(a, b);
+ case '*':
+ return Matrix.multiply(a, b);
+ }
+ return null;
+ }
+
+ public static void output(List decides) {
+ for (final Matrix matrix : decides) {
+ for (int i = 0; i < matrix.m; i++) {
+ for (int j = 0; j < matrix.m; j++) {
+ System.out.print(matrix.data[i][j] + (j == matrix.m - 1 ? "" : " "));
+ }
+ System.out.print('\n');
+ } // 不需要多余空行
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ output(cal(reader()));
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ long nextLong() {
+ return Long.parseLong(next());
+ }
+
+ double nextDouble() {
+ return Double.parseDouble(next());
+ }
+
+ String nextLine() {
+ String str = "";
+ try {
+ str = br.readLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return str;
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1164/test/MainTest.java b/2018fall/lab_4/lab_4_1164/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1164/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
index 3db3cb6..aa78e0a 100644
--- a/2018fall/lab_4/pom.xml
+++ b/2018fall/lab_4/pom.xml
@@ -21,6 +21,7 @@
lab_4_1039
lab_4_1161
lab_4_1162
+ lab_4_1164
From 8687c5d677e2e39150f3a3a9b8c59149ddbd9827 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sat, 20 Sep 2025 16:47:07 +0000
Subject: [PATCH 06/46] feat: add 2018fall/lab4-1165
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/AGENTS.md | 17 ++
2018fall/lab_4/README.md | 1 +
2018fall/lab_4/lab_4_1165/README.md | 89 +++++++++
2018fall/lab_4/lab_4_1165/pom.xml | 22 +++
.../lab_4/lab_4_1165/resources/01.data.in | 8 +
.../lab_4/lab_4_1165/resources/01.data.out | 2 +
2018fall/lab_4/lab_4_1165/src/Main.java | 173 ++++++++++++++++++
2018fall/lab_4/lab_4_1165/test/MainTest.java | 45 +++++
2018fall/lab_4/pom.xml | 1 +
9 files changed, 358 insertions(+)
create mode 100644 2018fall/AGENTS.md
create mode 100644 2018fall/lab_4/lab_4_1165/README.md
create mode 100644 2018fall/lab_4/lab_4_1165/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1165/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1165/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1165/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1165/test/MainTest.java
diff --git a/2018fall/AGENTS.md b/2018fall/AGENTS.md
new file mode 100644
index 0000000..8858cf2
--- /dev/null
+++ b/2018fall/AGENTS.md
@@ -0,0 +1,17 @@
+## 注意事项
+
+1. 使用 JDK11 语法, 尽量使用现代数据结构, 尽量使用final var不可变变量
+2. 尽量遵守读-处理-输出分离的原则
+3. 尽量使用 `System.out.print('\n')` 来表示换行
+
+## 定义操作
+2. 定义对README进行的预处理
+
++ README.md内 `Description` 应该为 `## `, Input, Output, Sample Input, Sample Output, HINT 等均改写为 `### `
++ `Sample Input` `Sample Output`内里面的输入输出, 用 ``` log ``` 包裹
++ 注意去除/替换部分非中英文的字符
+
+2. 定义解答流程
+
++ 根据题目描述, 以及输入输出文件 data.in, data.out, 按照JDK11语法, 并遵守读-处理-输出分离的原则, 重写 Main.java
++ 使用题目约束, 在 Main.java 的 reader 内部加入 assert 判断
diff --git a/2018fall/lab_4/README.md b/2018fall/lab_4/README.md
index 4578969..4d3fa6c 100644
--- a/2018fall/lab_4/README.md
+++ b/2018fall/lab_4/README.md
@@ -8,3 +8,4 @@
+ [x] problem D: lab_4_1162
+ [ ] problem E: lab_4_1163
+ [x] problem F: lab_4_1164
++ [x] problem G: lab_4_1165
diff --git a/2018fall/lab_4/lab_4_1165/README.md b/2018fall/lab_4/lab_4_1165/README.md
new file mode 100644
index 0000000..b99087f
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1165/README.md
@@ -0,0 +1,89 @@
+## Description
+
+There are T test cases, for each test case, there are n (1<=n<=400000) operations for a stack. And there are only two operations in this problem.
+
+The following operations are:
+
+1. push x
+2. pop
+
+For operation 1 you are asked to push x(1<=x<=100000000) in to the stack.
+
+For operation 2 you are asked to pop out the top element of the stack and print the maximum number of the stack - minimum number in the stack.
+
+### Input
+
+The first line is an integer T(1<=T<=5).
+
+For each case, the first line is n(1<=n<=400000) , which is the number of operations, then the following are n lines containing either operation 1 or operation 2.
+
+It is not guaranteed that whether the stack is empty. If the stack is empty and you are asked to pop the element, you can just ignore the operation, but still need to print the corresponding answer.
+
+### Output
+
+For each pop operation, print the (MAX - MIN) value in the remaining stack. If the stack is empty, print 0.
+
+For each test case, print each answer in a line.
+
+### Sample Input
+
+```log
+1
+6
+push 387
+pop
+push 278
+push 416
+push 111
+pop
+```
+
+### Sample Output
+
+```log
+0
+138
+```
+
+### HINT
+
+Hint: 0 is because the stack is empty after the first pop.
+
+138 is calculated by 416 - 278 = 138.
+
+## 解答
+
+尽管题面的英语表达有些...一言难尽(例如 "It is not guaranteed that whether the stack is empty"),
+
+但其核心是一个经典的“最小/最大栈”问题。题目要求在每次 `pop` 操作后,都能在 O(1) 时间内得到栈中剩余元素的最大值与最小值的差。
+
+### 核心算法: 最小/最大栈
+
+如果每次 `pop` 后都遍历整个栈来寻找最大和最小值,那么单次操作的复杂度将是 O(n),在 `n` 达到 400,000 的情况下必然超时。
+
+正确的解法是使用两个辅助栈(`minStack` 和 `maxStack`)与主栈 `values` 同步操作:
+- `push(x)`:
+ - `values` 正常推入 `x`。
+ - `minStack` 推入 `x` 与当前 `minStack` 栈顶的较小者。
+ - `maxStack` 推入 `x` 与当前 `maxStack` 栈顶的较大者。
+- `pop()`:
+ - 三个栈同时 `pop`。
+
+通过这种方式,`minStack` 和 `maxStack` 的栈顶永远分别是当前主栈中的最小值和最大值,使得我们可以在 O(1) 时间内完成查询。
+
+### 性能瓶颈: 从 TLE 到 AC 的关键
+
+即便算法复杂度最优,在 Java 中处理海量数据时,实现细节也至关重要。我们最初的尝试因为“超时(TLE)”而失败,其根本原因在于Integer 的自动装箱/拆箱。
+
+当我们试图遵循“读-处理-分离”原则,将 400,000 个操作存入 `List` 时,Java 实际上在堆内存中创建了 400,000 个 `Integer` 对象。这带来了巨大的性能损耗:
+1. 内存开销: 每个 `Integer` 对象都比原始的 `int` 占用更多内存。
+2. 时间开销: 频繁的自动装箱(`int` -> `Integer`)和后续处理中的自动拆箱(`Integer` -> `int`)本身就需要时间。
+3. 缓存失效: `List` 在内存中存储的是指向各个 `Integer` 对象的引用,这些对象在内存中散乱分布,导致 CPU 缓存命中率极低,处理器需要不断从主内存中抓取数据。
+
+最终的解决方案是,在保持“读-处理-分离”结构的同时,将数据载体从 `List` 换成了原始的 `int[]` 数组。`int[]` 在内存中是一块连续的空间,没有任何对象开销,这使得数据处理速度得到了质的飞跃,从而通过了时间限制。
+
+### 展望: Project Valhalla
+
+这个过程也凸显了 Java 当前在处理大规模数据时的痛点。我们之所以要费尽心机地使用 `int[]`,就是因为缺少一种既有对象特性、又有原始类型性能的“值类型”。
+
+这正是 Project Valhalla 致力于解决的问题。它计划为 Java 引入“值类型”(Value Types),允许开发者创建行为类似 `int` 但结构更丰富的类型。如果 Valhalla 落地,我们或许就能优雅地使用 `List` 这样的面向对象结构,而无需再为装箱带来的性能损耗而烦恼。我们拭目以待。
diff --git a/2018fall/lab_4/lab_4_1165/pom.xml b/2018fall/lab_4/lab_4_1165/pom.xml
new file mode 100644
index 0000000..d01a1ac
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1165/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab3
+ lab_4_1165
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1165/resources/01.data.in b/2018fall/lab_4/lab_4_1165/resources/01.data.in
new file mode 100644
index 0000000..e5c81d2
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1165/resources/01.data.in
@@ -0,0 +1,8 @@
+1
+6
+push 387
+pop
+push 278
+push 416
+push 111
+pop
diff --git a/2018fall/lab_4/lab_4_1165/resources/01.data.out b/2018fall/lab_4/lab_4_1165/resources/01.data.out
new file mode 100644
index 0000000..5bcfcc1
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1165/resources/01.data.out
@@ -0,0 +1,2 @@
+0
+138
diff --git a/2018fall/lab_4/lab_4_1165/src/Main.java b/2018fall/lab_4/lab_4_1165/src/Main.java
new file mode 100644
index 0000000..d1d6b64
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1165/src/Main.java
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+
+public final class Main {
+
+ private static final int POP_SENTINEL = 0;
+
+ private static final int PUSH = -1;
+ private static final int POP = -2;
+
+ /**
+ * Reads all test cases and their operations into a lightweight primitive array format.
+ * 'push x' is stored as x.
+ * 'pop' is stored as POP_SENTINEL (0).
+ * This avoids creating millions of Integer objects, which is a major performance bottleneck.
+ */
+ public static List reader() throws Exception {
+ try (final var in = new FastReader();) {
+ final int testcases = in.nextInt();
+ assert (testcases >= 1 && testcases <= 5) : "T must be between 1 and 5";
+ final List allCases = new ArrayList<>(testcases);
+
+ for (int t = 0; t < testcases; t++) {
+ final int n = in.nextInt();
+ assert (n >= 1 && n <= 400000) : "n must be between 1 and 400000";
+ final int[] operations = new int[n];
+ for (int i = 0; i < n; i++) {
+ if (PUSH == in.nextOperation()) {
+ final int x = in.nextInt();
+ assert (x >= 1 && x <= 100000000) : "x must be between 1 and 100,000,000";
+ operations[i] = x;
+ } else {
+ operations[i] = POP_SENTINEL;
+ }
+ }
+ allCases.add(operations);
+ }
+ return allCases;
+ }
+ }
+
+ /**
+ * Processes all test cases and returns the results for each "pop" operation.
+ * The algorithm is already O(1) per operation, which is optimal.
+ */
+ public static List> cal(final List allCases) {
+ final List> allResults = new ArrayList<>();
+
+ for (final var operations : allCases) {
+ final Deque values = new ArrayDeque<>();
+ final Deque minStack = new ArrayDeque<>();
+ final Deque maxStack = new ArrayDeque<>();
+ final List caseResults = new ArrayList<>();
+
+ for (final int op : operations) {
+ if (op != POP_SENTINEL) { // Push operation
+ values.push(op);
+ if (minStack.isEmpty()) {
+ minStack.push(op);
+ maxStack.push(op);
+ } else {
+ minStack.push(Math.min(op, minStack.peek()));
+ maxStack.push(Math.max(op, maxStack.peek()));
+ }
+ } else { // Pop operation
+ if (values.isEmpty()) {
+ caseResults.add(0);
+ } else {
+ values.pop();
+ minStack.pop();
+ maxStack.pop();
+
+ if (values.isEmpty()) {
+ caseResults.add(0);
+ } else {
+ caseResults.add(maxStack.peek() - minStack.peek());
+ }
+ }
+ }
+ }
+ allResults.add(caseResults);
+ }
+ return allResults;
+ }
+
+ /**
+ * Prints the results to standard output using a StringBuilder for performance.
+ */
+ public static void output(final List> allResults) {
+ final var sb = new StringBuilder();
+ for (final var caseResults : allResults) {
+ for (final var result : caseResults) {
+ sb.append(result).append('\n');
+ }
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(String[] args) throws Exception {
+ output(cal(reader()));
+ }
+
+ // High-performance buffered reader, optimized for this problem
+ private static final class FastReader implements AutoCloseable {
+ private final DataInputStream din;
+ private final byte[] buffer;
+ private int bufferPointer, bytesRead;
+
+ public FastReader() {
+ din = new DataInputStream(System.in);
+ buffer = new byte[1 << 16]; // 64KB buffer
+ bufferPointer = bytesRead = 0;
+ }
+
+ public int nextOperation() throws IOException {
+ byte c;
+ while ((c = read()) <= ' ') ; // Skip whitespace
+
+ // First char must be 'p'
+ byte secondChar = read();
+ if (secondChar == 'o') {
+ read(); // consume 'p'
+ return POP;
+ } else { // must be 'u'
+ read(); // consume 's'
+ read(); // consume 'h'
+ return PUSH;
+ }
+ }
+
+ public int nextInt() throws IOException {
+ int ret = 0;
+ byte c = read();
+ while (c <= ' ') {
+ c = read();
+ }
+ boolean neg = (c == '-');
+ if (neg) {
+ c = read();
+ }
+ do {
+ ret = ret * 10 + c - '0';
+ } while ((c = read()) >= '0' && c <= '9');
+ return neg ? -ret : ret;
+ }
+
+ private void fillBuffer() throws IOException {
+ bytesRead = din.read(buffer, bufferPointer = 0, buffer.length);
+ if (bytesRead == -1) {
+ buffer[0] = -1;
+ }
+ }
+
+ private byte read() throws IOException {
+ if (bufferPointer == bytesRead) {
+ fillBuffer();
+ }
+ return buffer[bufferPointer++];
+ }
+
+ @Override
+ public void close() throws IOException {
+ din.close();
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1165/test/MainTest.java b/2018fall/lab_4/lab_4_1165/test/MainTest.java
new file mode 100644
index 0000000..36bf8b1
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1165/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws Exception {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
index aa78e0a..025fef2 100644
--- a/2018fall/lab_4/pom.xml
+++ b/2018fall/lab_4/pom.xml
@@ -22,6 +22,7 @@
lab_4_1161
lab_4_1162
lab_4_1164
+ lab_4_1165
From 4197a37c75521e0d9629e73d616a73ff6c4db37d Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 03:23:11 +0000
Subject: [PATCH 07/46] =?UTF-8?q?build:=20=E7=A6=81=E7=94=A8=20windows=20?=
=?UTF-8?q?=E8=BF=9C=E7=A8=8B=E6=9E=84=E5=BB=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
.gitattributes | 2 ++
.github/workflows/pull_request.yml | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/.gitattributes b/.gitattributes
index c72d4d0..0b5be87 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -44,3 +44,5 @@
*.so binary
*.war binary
*.jks binary
+
+*.csv text diff=csv eol=lf
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 52c671d..cb4e73f 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -18,7 +18,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ ubuntu-latest ,windows-latest ]
+ os: [ ubuntu-latest ]
steps:
- name: checkout code
uses: actions/checkout@v5
From 1177998cd80eca3f79a00d6aae084a4797cdae02 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 03:24:22 +0000
Subject: [PATCH 08/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?4-1163=20=E4=BB=A5=E5=8F=8A=202018fall/lab4=20=E6=95=B4?=
=?UTF-8?q?=E4=BD=93=E8=AF=84=E4=BB=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/AGENTS.md | 15 ++-
2018fall/lab_4/README.md | 68 ++++++++++-
2018fall/lab_4/lab_4_1039/pom.xml | 2 +-
2018fall/lab_4/lab_4_1159/pom.xml | 2 +-
2018fall/lab_4/lab_4_1161/pom.xml | 2 +-
2018fall/lab_4/lab_4_1162/pom.xml | 2 +-
2018fall/lab_4/lab_4_1163/README.md | 57 ++++++++++
2018fall/lab_4/lab_4_1163/pom.xml | 22 ++++
.../lab_4/lab_4_1163/resources/01.data.in | 5 +
.../lab_4/lab_4_1163/resources/01.data.out | 2 +
2018fall/lab_4/lab_4_1163/src/Main.java | 106 ++++++++++++++++++
2018fall/lab_4/lab_4_1163/test/MainTest.java | 45 ++++++++
2018fall/lab_4/lab_4_1164/pom.xml | 2 +-
2018fall/lab_4/lab_4_1165/README.md | 4 +-
2018fall/lab_4/lab_4_1165/pom.xml | 2 +-
2018fall/lab_4/pom.xml | 1 +
2018fall/lab_4/submit.csv | 9 ++
17 files changed, 337 insertions(+), 9 deletions(-)
create mode 100644 2018fall/lab_4/lab_4_1163/README.md
create mode 100644 2018fall/lab_4/lab_4_1163/pom.xml
create mode 100644 2018fall/lab_4/lab_4_1163/resources/01.data.in
create mode 100644 2018fall/lab_4/lab_4_1163/resources/01.data.out
create mode 100644 2018fall/lab_4/lab_4_1163/src/Main.java
create mode 100644 2018fall/lab_4/lab_4_1163/test/MainTest.java
create mode 100644 2018fall/lab_4/submit.csv
diff --git a/2018fall/AGENTS.md b/2018fall/AGENTS.md
index 8858cf2..d7c2dd8 100644
--- a/2018fall/AGENTS.md
+++ b/2018fall/AGENTS.md
@@ -3,9 +3,22 @@
1. 使用 JDK11 语法, 尽量使用现代数据结构, 尽量使用final var不可变变量
2. 尽量遵守读-处理-输出分离的原则
3. 尽量使用 `System.out.print('\n')` 来表示换行
+4. 不使用任何中文标点
+
++ 以使用 `"` 为荣, 以使用 `“`, `”` 为耻
++ 以使用 `'` 为荣, 以使用 `‘`, `’` 为耻
++ 以使用 `,` 为荣, 以使用 `,` 为耻
++ 以使用 `.` 为荣, 以使用 `。` 为耻
++ 以使用 `:` 为荣, 以使用 `:` 为耻
++ 以使用 `;` 为荣, 以使用 `;` 为耻
++ 以使用 `!` 为荣, 以使用 `!` 为耻
++ 以使用 `?` 为荣, 以使用 `?` 为耻
+
+> 注意给英文字符留出一个空格的空白
## 定义操作
-2. 定义对README进行的预处理
+
+1. 定义对README进行的预处理
+ README.md内 `Description` 应该为 `## `, Input, Output, Sample Input, Sample Output, HINT 等均改写为 `### `
+ `Sample Input` `Sample Output`内里面的输入输出, 用 ``` log ``` 包裹
diff --git a/2018fall/lab_4/README.md b/2018fall/lab_4/README.md
index 4d3fa6c..4b36224 100644
--- a/2018fall/lab_4/README.md
+++ b/2018fall/lab_4/README.md
@@ -6,6 +6,72 @@
+ [x] problem B: lab_4_1039
+ [x] problem C: lab_4_1161
+ [x] problem D: lab_4_1162
-+ [ ] problem E: lab_4_1163
++ [x] problem E: lab_4_1163
+ [x] problem F: lab_4_1164
+ [x] problem G: lab_4_1165
+
+## 总体评价
+
+> 这套题目的设计者, 似乎不是想教会学生什么, 而是想告诉他们"你们还有多少东西不会". 对于一个刚学完基础编程, 对"时间复杂度"只有模糊概念的学生来说, 这次实验的体验曲线大概是这样的:
+> 1. A 题: 虚假的希望. "哦, 好像不难, 我能做出来!"
+> 2. B 题: 进入正轨. "这才是课上讲的栈, 逻辑有点绕, 但总算过了."
+> 3. C/D/E 题: 思维的挑战. "为什么会 TLE? 我的逻辑没错啊? 是不是有什么巧妙的办法?"
+> 4. F/G 题: 彻底绝望. "这代码也太复杂了!"
+
+---
+
+## 各题目分析
+
+### 问题 A
+
+题面: 在一个字符串中寻找 "lanran" 作为子序列.
+
+这是所有题目中最"仁慈"的一个, AC 率最高 (约 31.6%). 它甚至不是一个栈或队列问题, 只是一个简单的字符串遍历.
+
+### 问题 B
+
+题面: 经典的三种括号 `()[]{}` 匹配问题.
+
+真正的栈应用入门题, 完全符合教科书的经典模型. AC 率约为 20.3%, 显著的 WA 和 RE 数量说明, 即便是经典问题, 学生在处理逻辑细节和边界情况时依然会遇到麻烦.
+
+### ⛰️ 问题 C, D, E: 算法思维的试金石
+
+这三道题开始脱离纯粹的数据结构模板, 转向对特定算法模型的考察, 是区分学生思维能力的关键.
+
+C 题 (`lab_4_1161`, 组合计数): 要求在 `O(n)` 时间内解决一个组合计数问题. 暴力解法会轻易导致 TLE, 迫使学生思考双指针/滑动窗口等优化技巧.
+D 题 (`lab_4_1162`, 迷宫寻路): 一个有趣的模拟题, 需要通过全排列来暴力枚举所有可能的方向映射, 考察了代码的组织和模拟能力.
+E 题 (`lab_4_1163`, 古老的蜘蛛牌): 用 `n <= 300,000` 的数据规模, 将一个看似简单的模拟题, 变成了对栈+贪心 `O(n)` 解法的硬性考察.
+
+### 👹 问题 F (`lab_4_1164`) & G (`lab_4_1165`): 复杂度的终极考验
+
+这两道题是本次实验的"劝退"核心, 它们不仅要求最优的时间复杂度, 还对代码实现和性能细节提出了极高要求.
+
+F 题 (矩阵表达式求值):
+
+这是本次实验最"阴险"的题目. 它将两个复杂的知识点 (中缀表达式求值 + 矩阵运算) 结合在一起. 学生不仅要写对双栈算法, 还要正确实现矩阵的各种运算.
+
+`submit.csv` 中惊人的提交次数 (2841次) 和海量的 RE/PE/WA
+
+G 题 (最小/最大栈):
+
+`n <= 400,000` 的操作次数, 要求在每次 `pop` 后 O(1) 返回栈内最大减最小值. 这是一道考察"最小/最大栈" (使用辅助栈) 的模板题.
+
+它不仅考察了特定的数据结构设计, 还因为海量数据和 Java 的装箱/拆箱性能问题, 给学生上了关于底层性能优化的深刻一课.
+
+---
+
+## 总结性批判
+
+难度设计断层严重: 从基础的字符串和栈应用, 直接跳跃到需要双指针、贪心、高级数据结构设计 (如最小/最大栈) 和复杂模拟的难题, 中间缺少了足够的过渡.
+
+过于强调"竞赛思维": 整套题目的设计思路, 尤其是对时间复杂度的极致压榨, 完全是算法竞赛 (ACM/ICPC) 的风格. 这对于旨在培养大多数学生基本编程素养的常规课程来说, 目标可能出现了偏差.
+
+考察范围过广: 一次实验同时考察了字符串、栈、队列、表达式求值、矩阵、贪心、双指针、高级数据结构设计等大量知识点, 并且要求极高的实现精度, 这对学生的消化和吸收能力是巨大的挑战.
+
+---
+
+## 结论
+
+> 这套题目作为选拔性测试是极其成功的, 它能精准地筛选出那些具有算法天赋、思维缜密、基础扎实的学生.
+>
+> 但作为一次面向全体学生的'教学', 它在引导和启发方面的作用是负面的. 可能会打击大部分学生的学习热情, 而非激发他们的兴趣.
diff --git a/2018fall/lab_4/lab_4_1039/pom.xml b/2018fall/lab_4/lab_4_1039/pom.xml
index d5cb25e..f003f06 100644
--- a/2018fall/lab_4/lab_4_1039/pom.xml
+++ b/2018fall/lab_4/lab_4_1039/pom.xml
@@ -9,7 +9,7 @@
${revision}
./../pom.xml
- nanoseeds.algorithm-template.2018fall.lab3
+ nanoseeds.algorithm-template.2018fall.lab4
lab_4_1039
${revision}
${project.groupId}.${project.artifactId}
diff --git a/2018fall/lab_4/lab_4_1159/pom.xml b/2018fall/lab_4/lab_4_1159/pom.xml
index d07f01d..a86d28c 100644
--- a/2018fall/lab_4/lab_4_1159/pom.xml
+++ b/2018fall/lab_4/lab_4_1159/pom.xml
@@ -9,7 +9,7 @@
${revision}
./../pom.xml
- nanoseeds.algorithm-template.2018fall.lab3
+ nanoseeds.algorithm-template.2018fall.lab4
lab_4_1159
${revision}
${project.groupId}.${project.artifactId}
diff --git a/2018fall/lab_4/lab_4_1161/pom.xml b/2018fall/lab_4/lab_4_1161/pom.xml
index b4047b9..bb63962 100644
--- a/2018fall/lab_4/lab_4_1161/pom.xml
+++ b/2018fall/lab_4/lab_4_1161/pom.xml
@@ -9,7 +9,7 @@
${revision}
./../pom.xml
- nanoseeds.algorithm-template.2018fall.lab3
+ nanoseeds.algorithm-template.2018fall.lab4
lab_4_1161
${revision}
${project.groupId}.${project.artifactId}
diff --git a/2018fall/lab_4/lab_4_1162/pom.xml b/2018fall/lab_4/lab_4_1162/pom.xml
index 447492b..2ce66ba 100644
--- a/2018fall/lab_4/lab_4_1162/pom.xml
+++ b/2018fall/lab_4/lab_4_1162/pom.xml
@@ -9,7 +9,7 @@
${revision}
./../pom.xml
- nanoseeds.algorithm-template.2018fall.lab3
+ nanoseeds.algorithm-template.2018fall.lab4
lab_4_1162
${revision}
${project.groupId}.${project.artifactId}
diff --git a/2018fall/lab_4/lab_4_1163/README.md b/2018fall/lab_4/lab_4_1163/README.md
new file mode 100644
index 0000000..2fa7e70
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1163/README.md
@@ -0,0 +1,57 @@
+## Description
+
+Ancient Spider is a very popular card game, and Vince loves to play it. Today he wants to play Ancient Spider again, but he changes the rule to make it more exciting. At the beginning of the game, Vince has an empty slot on the table. There are n different cards numbered from 1 to n, and Vince will receive them one by one in a given order and put the cards onto the top of the slot. At any time, Vince can pick up a card from the top of slot and discard it. If Vince discards all n cards, the game is over. Vince wants you to help him find the smallest lexicographical order among all possible discarding orders at the end of the game.
+If you don't know the concept of lexicographical order, you can see the reference in the following link: https://en.wikipedia.org/wiki/Lexicographical_order
+### Input
+The first line is an integer T, which is the number of test cases.
+Each test case contains two lines. The first line has an integer, n.
+The second line contains a sequence A of length n, which is a permutation of 1 to n, representing the order Vince receives the cards.
+(1<=T<=5, 1<=n<=300000)
+### Output
+
+For each test case, print n integers in a line, which is the order discarding the card with the smallest lexicographical order.
+### Sample Input
+
+```log
+2
+3
+1 3 2
+4
+3 4 1 2
+```
+
+### Sample Output
+
+```log
+1 2 3
+1 2 4 3
+```
+
+## 解答
+
+> 未经 OJ 验证
+
+本题的目标是找到一个卡牌游戏中的最小字典序弃牌顺序。游戏规则如下:按给定顺序发牌,玩家可以将牌放到一个牌堆(槽)的顶部,也可以随时从牌堆顶部取出一张牌丢弃。
+
+为了获得字典序最小的弃牌序列,我们应该遵循一个贪心策略:**一旦当前可以弃置的牌是接下来期望弃置的最小的牌,就立即弃置它**。
+
+具体算法如下:
+1. 我们维护一个期望弃置的牌的编号 `nextExpectedCard`,初始值为 1。
+2. 我们使用一个栈来模拟牌堆。
+3. 依次处理发到手中的每张牌 `card`:
+ a. 将 `card` 压入栈中。
+ b. 循环检查栈顶的牌:只要栈不为空且栈顶的牌等于 `nextExpectedCard`,就说明我们可以弃置这张牌以满足最小字典序。我们立即将其从栈中弹出,记录到输出序列中,并将 `nextExpectedCard` 加一。
+4. 当所有牌都发完后,栈中可能还剩下一些牌。此时,为了完成游戏,我们必须将它们按从栈顶到栈底的顺序依次弃置。
+
+这个策略保证了我们总是尽早地弃置当前可能弃置的最小编号的牌,从而确保最终得到的弃牌序列的字典序是最小的。
+
+例如,对于输入序列 `3 4 1 2`:
+1. 收到 `3`,入栈。栈:`[3]`。
+2. 收到 `4`,入栈。栈:`[3, 4]`。
+3. 收到 `1`,入栈。栈:`[3, 4, 1]`。此时栈顶是 `1`,等于 `nextExpectedCard`,所以弹出 `1`。输出:`1`。`nextExpectedCard` 变为 `2`。栈:`[3, 4]`。
+4. 收到 `2`,入栈。栈:`[3, 4, 2]`。此时栈顶是 `2`,等于 `nextExpectedCard`,所以弹出 `2`。输出:`1 2`。`nextExpectedCard` 变为 `3`。栈:`[3, 4]`。
+5. 检查栈顶,是 `4`,不等于 `nextExpectedCard` (3),不做操作。
+6. 所有牌处理完毕。将栈中剩余的牌 `4` 和 `3` 依次弹出。输出:`1 2 4 3`。
+
+最终得到的最小字典序序列为 `1 2 4 3`。
+
diff --git a/2018fall/lab_4/lab_4_1163/pom.xml b/2018fall/lab_4/lab_4_1163/pom.xml
new file mode 100644
index 0000000..c87cb5c
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1163/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_4
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab4
+ lab_4_1163
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_4/lab_4_1163/resources/01.data.in b/2018fall/lab_4/lab_4_1163/resources/01.data.in
new file mode 100644
index 0000000..459d0f9
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1163/resources/01.data.in
@@ -0,0 +1,5 @@
+2
+3
+1 3 2
+4
+3 4 1 2
diff --git a/2018fall/lab_4/lab_4_1163/resources/01.data.out b/2018fall/lab_4/lab_4_1163/resources/01.data.out
new file mode 100644
index 0000000..cfe3460
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1163/resources/01.data.out
@@ -0,0 +1,2 @@
+1 2 3
+1 2 4 3
diff --git a/2018fall/lab_4/lab_4_1163/src/Main.java b/2018fall/lab_4/lab_4_1163/src/Main.java
new file mode 100644
index 0000000..6eeadfb
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1163/src/Main.java
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ final int n;
+ final int[] arrivalOrder;
+
+ public TestCase(int n, int[] arrivalOrder) {
+ this.n = n;
+ this.arrivalOrder = arrivalOrder;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ final List cases = new ArrayList<>(testCases);
+ for (int t = 0; t < testCases; t++) {
+ final int n = in.nextInt();
+ final int[] arrivalOrder = new int[n];
+ for (int i = 0; i < n; i++) {
+ arrivalOrder[i] = in.nextInt();
+ }
+ cases.add(new TestCase(n, arrivalOrder));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ final Deque stack = new ArrayDeque<>();
+ final int[] output = new int[tc.n];
+ int outputIndex = 0;
+ int nextExpectedCard = 1;
+
+ for (int card : tc.arrivalOrder) {
+ stack.push(card);
+ while (!stack.isEmpty() && stack.peek() == nextExpectedCard) {
+ output[outputIndex++] = stack.pop();
+ nextExpectedCard++;
+ }
+ }
+
+ while (!stack.isEmpty()) {
+ output[outputIndex++] = stack.pop();
+ }
+ results.add(output);
+ }
+ return results;
+ }
+
+ public static void output(List results) {
+ final var sb = new StringBuilder();
+ for (final int[] result : results) {
+ for (int i = 0; i < result.length; i++) {
+ sb.append(result[i]);
+ if (i < result.length - 1) {
+ sb.append(" ");
+ }
+ }
+ sb.append('\n');
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
+
diff --git a/2018fall/lab_4/lab_4_1163/test/MainTest.java b/2018fall/lab_4/lab_4_1163/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_4/lab_4_1163/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_4/lab_4_1164/pom.xml b/2018fall/lab_4/lab_4_1164/pom.xml
index acde97a..37f4f7a 100644
--- a/2018fall/lab_4/lab_4_1164/pom.xml
+++ b/2018fall/lab_4/lab_4_1164/pom.xml
@@ -9,7 +9,7 @@
${revision}
./../pom.xml
- nanoseeds.algorithm-template.2018fall.lab3
+ nanoseeds.algorithm-template.2018fall.lab4
lab_4_1164
${revision}
${project.groupId}.${project.artifactId}
diff --git a/2018fall/lab_4/lab_4_1165/README.md b/2018fall/lab_4/lab_4_1165/README.md
index b99087f..43e4bb4 100644
--- a/2018fall/lab_4/lab_4_1165/README.md
+++ b/2018fall/lab_4/lab_4_1165/README.md
@@ -80,7 +80,9 @@ Hint: 0 is because the stack is empty after the first pop.
2. 时间开销: 频繁的自动装箱(`int` -> `Integer`)和后续处理中的自动拆箱(`Integer` -> `int`)本身就需要时间。
3. 缓存失效: `List` 在内存中存储的是指向各个 `Integer` 对象的引用,这些对象在内存中散乱分布,导致 CPU 缓存命中率极低,处理器需要不断从主内存中抓取数据。
-最终的解决方案是,在保持“读-处理-分离”结构的同时,将数据载体从 `List` 换成了原始的 `int[]` 数组。`int[]` 在内存中是一块连续的空间,没有任何对象开销,这使得数据处理速度得到了质的飞跃,从而通过了时间限制。
+最终解决方案是,在保持“读-处理-分离”结构的同时,将数据载体从 `List` 换成了原始的 `int[]` 数组。`int[]` 在内存中是一块连续的空间,没有任何对象开销,这使得数据处理速度得到了质的飞跃,从而通过了时间限制。
+
+> 换一个角度想, 可以用C++重写一遍, STL容器可没拆箱装箱的坏毛病
### 展望: Project Valhalla
diff --git a/2018fall/lab_4/lab_4_1165/pom.xml b/2018fall/lab_4/lab_4_1165/pom.xml
index d01a1ac..5352f71 100644
--- a/2018fall/lab_4/lab_4_1165/pom.xml
+++ b/2018fall/lab_4/lab_4_1165/pom.xml
@@ -9,7 +9,7 @@
${revision}
./../pom.xml
- nanoseeds.algorithm-template.2018fall.lab3
+ nanoseeds.algorithm-template.2018fall.lab4
lab_4_1165
${revision}
${project.groupId}.${project.artifactId}
diff --git a/2018fall/lab_4/pom.xml b/2018fall/lab_4/pom.xml
index 025fef2..d29b2ed 100644
--- a/2018fall/lab_4/pom.xml
+++ b/2018fall/lab_4/pom.xml
@@ -21,6 +21,7 @@
lab_4_1039
lab_4_1161
lab_4_1162
+ lab_4_1163
lab_4_1164
lab_4_1165
diff --git a/2018fall/lab_4/submit.csv b/2018fall/lab_4/submit.csv
new file mode 100644
index 0000000..25d551b
--- /dev/null
+++ b/2018fall/lab_4/submit.csv
@@ -0,0 +1,9 @@
+AC, PE, WA, TLE, MLE, OLE, RE, CE, TR, Total , C, C++, Java
+A, 219, 129, 4, 5, 59, 277, 693, 77, 154, 462
+B, 201, 133, 150, 75, 432, 991, 134, 152, 705
+C, 176, 769, 231, 8, 135, 170, 1352, 2841, 263, 659, 1919
+D, 149, 162, 2, 8, 30, 31, 468, 850, 161, 107, 582
+E, 46, 1, 325, 123, 4, 231, 90, 786, 1606, 134, 233, 1239
+F, 79, 7, 347, 9, 5, 2, 91, 34, 520, 1094, 38, 258, 798
+G, 190, 8, 342, 251, 4, 57, 138, 904, 1894, 119, 258, 1517
+Total, 1060, 16, 2207, 620, 17, 14, 699, 597, 4739, 9969, 926, 1821, 7222
From ce54a7961e3cc9f928259c840743ed1469c90097 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 03:43:26 +0000
Subject: [PATCH 09/46] feat: add 2018fall/lab5-1145
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/README.md | 29 +++++
2018fall/lab_5/lab_5_1145/README.md | 67 +++++++++++
2018fall/lab_5/lab_5_1145/pom.xml | 22 ++++
.../lab_5/lab_5_1145/resources/01.data.in | 7 ++
.../lab_5/lab_5_1145/resources/01.data.out | 1 +
2018fall/lab_5/lab_5_1145/src/Main.java | 113 ++++++++++++++++++
2018fall/lab_5/lab_5_1145/test/MainTest.java | 45 +++++++
2018fall/lab_5/pom.xml | 31 +++++
2018fall/lab_5/submit.csv | 9 ++
2018fall/pom.xml | 1 +
10 files changed, 325 insertions(+)
create mode 100644 2018fall/lab_5/README.md
create mode 100644 2018fall/lab_5/lab_5_1145/README.md
create mode 100644 2018fall/lab_5/lab_5_1145/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1145/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1145/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1145/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1145/test/MainTest.java
create mode 100644 2018fall/lab_5/pom.xml
create mode 100644 2018fall/lab_5/submit.csv
diff --git a/2018fall/lab_5/README.md b/2018fall/lab_5/README.md
new file mode 100644
index 0000000..c3153ef
--- /dev/null
+++ b/2018fall/lab_5/README.md
@@ -0,0 +1,29 @@
+# 2018fall-lab5
+
+Welcome to (autumn) DSAA lab 4! Enjoy this Lab!
+
+There are seven problems for you to solve. Two of them are bonus. Read the problem description carefully.
+
+Compulsory problems:
+
++ A(easy): 10
++ B(easy): 10
++ C(easy): 20
++ D(median): 25
++ E(median): 25
++ Bonus problem: F(hard): 30
++ Bonus problem: G(hard): 30
+
+Read the samples carefully can help you understand the problem.
+
+## Stack And Queue
+
++ [x] problem A: lab_5_1145
++ [ ] problem B: lab_5_1146
++ [ ] problem C: lab_5_1047
++ [ ] problem D: lab_5_1148
++ [ ] problem E: lab_5_1149
++ [ ] problem F: lab_5_1150
++ [ ] problem G: lab_5_1151
+
+## 总体评价
diff --git a/2018fall/lab_5/lab_5_1145/README.md b/2018fall/lab_5/lab_5_1145/README.md
new file mode 100644
index 0000000..868cbec
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1145/README.md
@@ -0,0 +1,67 @@
+## Description
+
+Hong likes the Rap of China. He has tried to write some lyrics. However, he didn't know how to judge a lyric.
+
+He thought that the more rhymes the better.
+
+The score of a lyric is equal to the length of the longest continued rhyming sentences.
+
+Two sentences are rhyming when the last letters of them are equal.
+
+Hong wants to know the score of the given lyrics.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 100), which is the number of test cases.
+
+For each test data:
+
+The first line contains an integer N (1 <= N <= 10^4) - the number of the sentences.
+
+Each of the next N lines contains a string s, which consists only of lowercase letters (no space). The length of each string doesn't exceed 100.
+
+### Output
+
+For each case please, print the length of the longest continued rhyming sentences.
+
+### Sample Input
+
+```log
+1
+5
+nikanzhegemian
+tayouchangyoukuan
+jiuxiangzhegewan
+tayoudayouyuan
+skrskr
+```
+
+### Sample Output
+
+```log
+4
+```
+
+## 解答
+
+本题的目标是计算歌词中连续押韵句子的最长长度. 根据定义, 如果两个句子的最后一个字母相同, 它们就押韵.
+
+这是一个简单的迭代问题, 我们可以通过一次遍历来解决, 算法复杂度为 O(N), 其中 N 是句子的数量.
+
+算法思路如下:
+1. 初始化两个计数器: `maxStreak` 用于记录全局最长的连续押韵长度, `currentStreak` 用于记录当前正在计算的连续押韵长度. 如果至少有一句话, 它们的初始值都应为 1.
+2. 从第二句话开始, 遍历整个句子列表.
+3. 在每一步, 比较当前句子和前一个句子的最后一个字母.
+ - 如果最后一个字母相同, 说明押韵仍在继续, 将 `currentStreak` 加 1.
+ - 如果最后一个字母不同, 说明连续押韵中断了. 此时, 我们需要将 `currentStreak` 的值与 `maxStreak` 比较, 更新 `maxStreak` 为两者中的较大者, 然后将 `currentStreak` 重置为 1 (因为新的句子本身构成了一个长度为 1 的新序列).
+4. 遍历结束后, 不要忘记最后再用 `currentStreak` 更新一次 `maxStreak`, 以处理最长的押韵序列恰好在歌词末尾结束的情况.
+5. 最终得到的 `maxStreak` 就是答案.
+
+例如, 对于示例输入:
+- `...mian`
+- `...kuan` (押韵, `currentStreak` = 2)
+- `...gewan` (押韵, `currentStreak` = 3)
+- `...yuan` (押韵, `currentStreak` = 4)
+- `...skr` (不押韵, `maxStreak` 更新为 4, `currentStreak` 重置为 1)
+
+最终结果为 4.
diff --git a/2018fall/lab_5/lab_5_1145/pom.xml b/2018fall/lab_5/lab_5_1145/pom.xml
new file mode 100644
index 0000000..e7712f3
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1145/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1145
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1145/resources/01.data.in b/2018fall/lab_5/lab_5_1145/resources/01.data.in
new file mode 100644
index 0000000..25ea412
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1145/resources/01.data.in
@@ -0,0 +1,7 @@
+1
+5
+nikanzhegemian
+tayouchangyoukuan
+jiuxiangzhegewan
+tayoudayouyuan
+skrskr
diff --git a/2018fall/lab_5/lab_5_1145/resources/01.data.out b/2018fall/lab_5/lab_5_1145/resources/01.data.out
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1145/resources/01.data.out
@@ -0,0 +1 @@
+4
diff --git a/2018fall/lab_5/lab_5_1145/src/Main.java b/2018fall/lab_5/lab_5_1145/src/Main.java
new file mode 100644
index 0000000..b217535
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1145/src/Main.java
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ // Using a static final class for JDK 11 compatibility
+ public static final class TestCase {
+ final char[] lastChars;
+
+ public TestCase(char[] lastChars) {
+ this.lastChars = lastChars;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 100) : "T must be between 1 and 100";
+ final List cases = new ArrayList<>(testCases);
+ for (int t = 0; t < testCases; t++) {
+ final int n = in.nextInt();
+ assert (n >= 1) && (n <= 10000) : "N must be between 1 and 10^4";
+ final char[] lastChars = new char[n];
+ for (int i = 0; i < n; i++) {
+ final String s = in.nextLine();
+ assert (s.length() <= 100) : "Sentence length must not exceed 100";
+ assert s.matches("[a-z]+") : "Sentence must consist only of lowercase letters";
+ lastChars[i] = s.charAt(s.length() - 1);
+ }
+ cases.add(new TestCase(lastChars));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ if (tc.lastChars.length == 0) {
+ results.add(0);
+ continue;
+ }
+
+ int maxStreak = 1;
+ int currentStreak = 1;
+ for (int i = 1; i < tc.lastChars.length; i++) {
+ if (tc.lastChars[i - 1] == tc.lastChars[i]) {
+ currentStreak++;
+ } else {
+ maxStreak = Math.max(maxStreak, currentStreak);
+ currentStreak = 1;
+ }
+ }
+ maxStreak = Math.max(maxStreak, currentStreak);
+ results.add(maxStreak);
+ }
+ return results;
+ }
+
+ public static void output(List decides) {
+ for (final var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ String nextLine() {
+ String str = "";
+ try {
+ str = br.readLine();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return str;
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1145/test/MainTest.java b/2018fall/lab_5/lab_5_1145/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1145/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
new file mode 100644
index 0000000..7b1b944
--- /dev/null
+++ b/2018fall/lab_5/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template
+ 2018fall
+ ${revision}
+ ./../pom.xml
+
+
+ pom
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+ lab_5_1145
+
+
+
+ nanoseeds.algorithm-template
+ test_include_package
+ ${revision}
+ test
+
+
+
+
diff --git a/2018fall/lab_5/submit.csv b/2018fall/lab_5/submit.csv
new file mode 100644
index 0000000..df03337
--- /dev/null
+++ b/2018fall/lab_5/submit.csv
@@ -0,0 +1,9 @@
+AC, PE, WA, TLE, MLE, OLE, RE, CE, TR, Total , C, C++, Java
+A, 207, 217, 2, 34, 26, 145, 631, 117, 109, 405
+B, 57, 1, 499, 2, 12, 2, 310, 44, 308, 1235, 74, 245, 916
+G, 46, 3, 206, 128, 9, 50, 20, 92, 554, 82, 94, 378
+E, 210, 5, 355, 134, 2, 52, 77, 15, 345, 1195, 81, 333, 781
+F, 167, 306, 152, 4, 69, 28, 391, 1117, 30, 213, 874
+D, 179, 618, 216, 1, 6, 151, 62, 810, 2043, 181, 312, 1550
+C, 228, 351, 311, 11, 3, 100, 73, 357, 1434, 134, 184, 1116
+Total, 1094, 9, 2552, 943, 26, 78, 791, 268, 2448, 8209, 699, 1490, 6020
diff --git a/2018fall/pom.xml b/2018fall/pom.xml
index 2427ebb..2b2411a 100644
--- a/2018fall/pom.xml
+++ b/2018fall/pom.xml
@@ -20,5 +20,6 @@
lab_2
lab_3
lab_4
+ lab_5
From 75854203c69245c7a175a572e9313e719403f100 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 03:55:34 +0000
Subject: [PATCH 10/46] feat: add 2018fall/lab5-1146
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/lab_5_1146/README.md | 76 +++++++++++
2018fall/lab_5/lab_5_1146/pom.xml | 22 ++++
.../lab_5/lab_5_1146/resources/01.data.in | 4 +
.../lab_5/lab_5_1146/resources/01.data.out | 1 +
2018fall/lab_5/lab_5_1146/src/Main.java | 123 ++++++++++++++++++
2018fall/lab_5/lab_5_1146/test/MainTest.java | 45 +++++++
2018fall/lab_5/pom.xml | 1 +
7 files changed, 272 insertions(+)
create mode 100644 2018fall/lab_5/lab_5_1146/README.md
create mode 100644 2018fall/lab_5/lab_5_1146/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1146/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1146/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1146/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1146/test/MainTest.java
diff --git a/2018fall/lab_5/lab_5_1146/README.md b/2018fall/lab_5/lab_5_1146/README.md
new file mode 100644
index 0000000..2c43580
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1146/README.md
@@ -0,0 +1,76 @@
+## Description
+
+Hong haves two strings s and t. The length of the string s equals n, the length of the string t equals m.
+
+The string s consists of lowercase letters and at most one wildcard character '*', while the string t consists only of lowercase letters.
+
+The wildcard character '*' in the string s (if any) can be replaced with an arbitrary sequence (possibly void sequence) of lowercase letters.
+
+If it is possible to replace a wildcard character '*' in s to obtain a string t, then the string t matches the pattern s.
+
+If the given string t matches the given string s, print "YES", otherwise print "NO".
+
+### Input
+
+The first line will be an integer T (1 <= T <= 10), which is the number of test cases.
+
+For each test data:
+
+The first line contains two integers n and m (1 <= n, m <= 2 * 10^5) — the length of the string s and the length of the string t, respectively.
+
+The second line contains string s of length n, which consists of lowercase letters and at most one wildcard character '*'.
+
+The third line contains string t of length m, which consists only of lowercase letters.
+
+### Output
+
+For each test cases, print "YES" (without quotes), if you can obtain the string t from the string s.
+
+Otherwise print "NO" (without quotes).
+
+### Sample Input
+
+```log
+1
+7 10
+aba*aba
+abazzzzaba
+```
+
+### Sample Output
+
+```log
+YES
+```
+
+## 解答
+
+本题要求我们判断一个字符串 `t` 是否匹配一个可能包含单个通配符 `*` 的模式字符串 `s`. 通配符 `*` 可以代表任意长度的任意字符序列 (包括空序列).
+
+这是一个典型的字符串匹配问题, 我们可以根据模式字符串 `s` 是否包含通配符 `*` 来分情况讨论.
+
+### 情况一: `s` 中没有通配符 `*`
+
+这是最简单的情况. 如果 `s` 中没有 `*`, 那么 `t` 必须与 `s` 完全相同才能匹配. 我们只需要直接比较两个字符串是否相等即可.
+
+### 情况二: `s` 中有通配符 `*`
+
+当 `s` 中存在 `*` 时, 我们可以将 `s` 分割成两部分:
+- `prefix`: `*` 号之前的部分.
+- `suffix`: `*` 号之后的部分.
+
+例如, 如果 `s` 是 `aba*aba`, 那么 `prefix` 就是 `aba`, `suffix` 也是 `aba`.
+
+要使 `t` 匹配 `s`, 必须同时满足以下条件:
+1. `t` 必须以 `prefix` 开头.
+2. `t` 必须以 `suffix` 结尾.
+3. `t` 的总长度必须至少是 `prefix` 和 `suffix` 长度之和. 这个条件确保了前缀和后缀在 `t` 中不会发生重叠, 中间可以由 `*` 所代表的字符序列 (哪怕是空序列) 连接.
+
+如果 `s` 的长度 (不计 `*`)大于 `t` 的长度, 那么无论如何都不可能匹配, 这是一个可以提前判断的剪枝条件.
+
+代码 `Main.java` 正是遵循了这种清晰的逻辑:
+- 首先检查 `s` 中是否存在 `*`.
+- 如果不存在, 直接进行字符串比较.
+- 如果存在, 则提取前缀和后缀, 然后使用 `startsWith()` 和 `endsWith()` 方法, 并结合长度检查, 来判断是否匹配.
+
+这种方法避免了复杂的循环和指针操作, 使得代码既高效又易于理解.
diff --git a/2018fall/lab_5/lab_5_1146/pom.xml b/2018fall/lab_5/lab_5_1146/pom.xml
new file mode 100644
index 0000000..806e5f7
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1146/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1146
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1146/resources/01.data.in b/2018fall/lab_5/lab_5_1146/resources/01.data.in
new file mode 100644
index 0000000..6800659
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1146/resources/01.data.in
@@ -0,0 +1,4 @@
+1
+7 10
+aba*aba
+abazzzzaba
diff --git a/2018fall/lab_5/lab_5_1146/resources/01.data.out b/2018fall/lab_5/lab_5_1146/resources/01.data.out
new file mode 100644
index 0000000..f033a50
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1146/resources/01.data.out
@@ -0,0 +1 @@
+YES
diff --git a/2018fall/lab_5/lab_5_1146/src/Main.java b/2018fall/lab_5/lab_5_1146/src/Main.java
new file mode 100644
index 0000000..14cac94
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1146/src/Main.java
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ // Using a static final class for JDK 11 compatibility
+ public static final class TestCase {
+ final int n;
+ final int m;
+ final String s;
+ final String t;
+
+ public TestCase(int n, int m, String s, String t) {
+ this.n = n;
+ this.m = m;
+ this.s = s;
+ this.t = t;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 10) : "T must be between 1 and 10";
+ final List cases = new ArrayList<>(testCases);
+ for (int i = 0; i < testCases; i++) {
+ final int n = in.nextInt();
+ final int m = in.nextInt();
+ assert (n >= 1) && (n <= 200000) : "n must be between 1 and 2*10^5";
+ assert (m >= 1) && (m <= 200000) : "m must be between 1 and 2*10^5";
+ final String s = in.next();
+ final String t = in.next();
+ assert s.length() == n : "s length should be n";
+ assert t.length() == m : "t length should be m";
+ cases.add(new TestCase(n, m, s, t));
+ }
+ return cases;
+ }
+ private static final String TRUE = "YES";
+ private static final String FALSE = "NO";
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ final int starIndex = tc.s.indexOf('*');
+
+ if (starIndex == -1) {
+ // Case 1: No wildcard
+ if (tc.s.equals(tc.t)) {
+ results.add(TRUE);
+ } else {
+ results.add(FALSE);
+ }
+ } else {
+ // Case 2: Wildcard exists
+ if (tc.n - 1 > tc.m) {
+ results.add(FALSE);
+ continue;
+ }
+
+ final String prefix = tc.s.substring(0, starIndex);
+ final String suffix = tc.s.substring(starIndex + 1);
+
+ if (tc.t.startsWith(prefix) && tc.t.endsWith(suffix)) {
+ // Ensure prefix and suffix don't overlap in the target string
+ if (prefix.length() + suffix.length() <= tc.m) {
+ results.add(TRUE);
+ } else {
+ results.add(FALSE);
+ }
+ } else {
+ results.add(FALSE);
+ }
+ }
+ }
+ return results;
+ }
+
+ public static void output(List decides) {
+ for (final var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ private static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ private Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1146/test/MainTest.java b/2018fall/lab_5/lab_5_1146/test/MainTest.java
new file mode 100644
index 0000000..91bd0de
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1146/test/MainTest.java
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
index 7b1b944..1ff9de0 100644
--- a/2018fall/lab_5/pom.xml
+++ b/2018fall/lab_5/pom.xml
@@ -18,6 +18,7 @@
${project.groupId}.${project.artifactId}
lab_5_1145
+ lab_5_1146
From 99529728ae21404fba516c8d881e9753268bc285 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 04:22:11 +0000
Subject: [PATCH 11/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?5-1047?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/lab_5_1047/README.md | 77 ++++++++++
2018fall/lab_5/lab_5_1047/pom.xml | 22 +++
.../lab_5/lab_5_1047/resources/01.data.in | 9 ++
.../lab_5/lab_5_1047/resources/01.data.out | 2 +
2018fall/lab_5/lab_5_1047/src/Main.java | 142 ++++++++++++++++++
2018fall/lab_5/lab_5_1047/test/MainTest.java | 103 +++++++++++++
2018fall/lab_5/pom.xml | 1 +
7 files changed, 356 insertions(+)
create mode 100644 2018fall/lab_5/lab_5_1047/README.md
create mode 100644 2018fall/lab_5/lab_5_1047/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1047/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1047/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1047/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1047/test/MainTest.java
diff --git a/2018fall/lab_5/lab_5_1047/README.md b/2018fall/lab_5/lab_5_1047/README.md
new file mode 100644
index 0000000..9cdac25
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1047/README.md
@@ -0,0 +1,77 @@
+## Description
+
+Give you a text S and a pattern P. You should print how many times P appears in S.
+
+### Input
+
+The first line will be an integer T, which is the number of test cases. (1 <= T <= 10)
+
+For each test case, the first line will be an integer n, which is the length of the text string.
+
+Then a line contains a text string S. |S| <= 1000000
+
+The third line will be an integer m, which is the length of the pattern string.
+
+Then a line contains a pattern string P. |P| <= |S|
+
+S and will only contain lower case English letters.
+
+### Output
+
+Print a number in a single line for each test case, which means how many times P appears in S.
+
+### Sample Input
+
+```log
+2
+15
+chenljnbwowowoo
+2
+wo
+14
+touristrealgod
+7
+tourist
+```
+
+### Sample Output
+
+```log
+3
+1
+```
+
+## 解答
+
+本题要求我们统计一个模式串 `P` 在一个文本串 `S` 中出现的次数.
+
+考虑到文本串 `S` 的长度可能达到 1,000,000, 使用朴素的暴力匹配算法 (时间复杂度为 O(|S| * |P|)) 会因为效率过低而超时. 因此, 解决这个问题的标准方法是采用 **KMP (Knuth-Morris-Pratt) 算法**, 它的时间复杂度为线性的 O(|S| + |P|), 能够轻松应对本题的数据规模.
+
+KMP 算法的核心思想是: 在匹配过程中发生不匹配时, 不回溯文本串 `S` 的指针, 而是利用已经匹配过的信息, 将模式串 `P` 的指针移动到一个合适的位置, 继续进行比较.
+
+这整个过程分为两步:
+
+### 1. 预处理模式串 `P`: 构建 `lps` 数组
+
+`lps` (Longest Proper Prefix which is also Suffix) 数组是 KMP 算法的关键. `lps[i]` 存储的是模式串 `P` 的子串 `P[0...i]` 中, 最长的相等的前缀和后缀的长度.
+
+- **前缀**: 不包含最后一个字符的子串.
+- **后缀**: 不包含第一个字符的子串.
+
+例如, 对于模式串 `P = "ababa"`:
+- `lps[0]` = 0 (子串 "a" 没有 proper 前后缀)
+- `lps[1]` = 0 (子串 "ab" 的前缀 "a" != 后缀 "b")
+- `lps[2]` = 1 (子串 "aba" 的前缀 "a" == 后缀 "a")
+- `lps[3]` = 2 (子串 "abab" 的前缀 "ab" == 后缀 "ab")
+- `lps[4]` = 3 (子串 "ababa" 的前缀 "aba" == 后缀 "aba")
+
+`computeLPSArray` 方法就是用来生成这个 `lps` 数组的.
+
+### 2. 匹配过程: `kmpSearch`
+
+在 `kmpSearch` 方法中, 我们使用两个指针 `i` (指向 `S`) 和 `j` (指向 `P`) 进行比较.
+- 如果 `S[i] == P[j]`, 两个指针都向前移动.
+- 如果 `j` 走到了 `P` 的末尾, 说明我们找到了一个完整的匹配. 此时, 计数器加一, 并且 `j` 指针利用 `lps` 数组跳转到 `lps[j-1]` 的位置, 继续寻找下一个可能的匹配.
+- 如果 `S[i] != P[j]`, `i` 指针保持不动, `j` 指针则根据 `lps` 数组回溯到 `lps[j-1]`, 从而跳过了一些明显不可能匹配的比较.
+
+通过这种方式, KMP 算法避免了暴力匹配中大量的重复比较, 实现了高效的字符串查找.
diff --git a/2018fall/lab_5/lab_5_1047/pom.xml b/2018fall/lab_5/lab_5_1047/pom.xml
new file mode 100644
index 0000000..b7dfd41
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1047/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1047
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1047/resources/01.data.in b/2018fall/lab_5/lab_5_1047/resources/01.data.in
new file mode 100644
index 0000000..293b60c
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1047/resources/01.data.in
@@ -0,0 +1,9 @@
+2
+15
+chenljnbwowowoo
+2
+wo
+14
+touristrealgod
+7
+tourist
diff --git a/2018fall/lab_5/lab_5_1047/resources/01.data.out b/2018fall/lab_5/lab_5_1047/resources/01.data.out
new file mode 100644
index 0000000..f00580c
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1047/resources/01.data.out
@@ -0,0 +1,2 @@
+3
+1
diff --git a/2018fall/lab_5/lab_5_1047/src/Main.java b/2018fall/lab_5/lab_5_1047/src/Main.java
new file mode 100644
index 0000000..48c677b
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1047/src/Main.java
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ // Using a static final class for JDK 11 compatibility
+ public static final class TestCase {
+ final String text;
+ final String pattern;
+
+ public TestCase(String text, String pattern) {
+ this.text = text;
+ this.pattern = pattern;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 10) : "T must be between 1 and 10";
+ final List cases = new ArrayList<>(testCases);
+ for (int i = 0; i < testCases; i++) {
+ final int n = in.nextInt();
+ final String s = in.next();
+ final int m = in.nextInt();
+ final String p = in.next();
+ assert s.length() == n : "Text length should be n";
+ assert p.length() == m : "Pattern length should be m";
+ assert n <= 1000000 : "|S| <= 1000000";
+ assert m <= n : "|P| <= |S|";
+ cases.add(new TestCase(s, p));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ results.add(kmpSearch(tc.text, tc.pattern));
+ }
+ return results;
+ }
+
+ public static int[] computeLPSArray(String pattern) {
+ final int m = pattern.length();
+ if (m == 0) {
+ return new int[0];
+ }
+ final int[] lps = new int[m];
+ for (int length = 0, i = 1; i < m; ) {
+ if (pattern.charAt(i) == pattern.charAt(length)) {
+ length++;
+ lps[i] = length;
+ i++;
+ } else {
+ if (length != 0) {
+ // This is the key: Do not increment i here.
+ // We must re-evaluate pattern[i] with the new (shorter) prefix length in the next loop iteration.
+ length = lps[length - 1];
+ } else {
+ lps[i] = 0;
+ i++;
+ }
+ }
+ }
+ return lps;
+ }
+
+ public static int kmpSearch(String text, String pattern) {
+ final int n = text.length();
+ final int m = pattern.length();
+ if (m == 0) {
+ return 0;
+ }
+ final int[] lps = computeLPSArray(pattern);
+ int i = 0; // pointer for text
+ int j = 0; // pointer for pattern
+ int count = 0;
+ while (i < n) {
+ if (pattern.charAt(j) == text.charAt(i)) {
+ i++;
+ j++;
+ }
+ if (j == m) {
+ count++;
+ j = lps[j - 1];
+ } else if (i < n && pattern.charAt(j) != text.charAt(i)) {
+ if (j != 0) {
+ j = lps[j - 1];
+ } else {
+ i++;
+ }
+ }
+ }
+ return count;
+ }
+
+ public static void output(List decides) {
+ for (final var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ public static final class Reader {
+ public final BufferedReader br;
+ public StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1047/test/MainTest.java b/2018fall/lab_5/lab_5_1047/test/MainTest.java
new file mode 100644
index 0000000..927ca75
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1047/test/MainTest.java
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+ @Test
+ public void testComputeLPSArray() {
+ // Test case 1: "ababa" -> [0, 0, 1, 2, 3]
+ Assertions.assertArrayEquals(new int[]{0, 0, 1, 2, 3}, Main.computeLPSArray("ababa"));
+
+ // Test case 2: "abcde" -> [0, 0, 0, 0, 0]
+ Assertions.assertArrayEquals(new int[]{0, 0, 0, 0, 0}, Main.computeLPSArray("abcde"));
+
+ // Test case 3: "aaaaa" -> [0, 1, 2, 3, 4]
+ Assertions.assertArrayEquals(new int[]{0, 1, 2, 3, 4}, Main.computeLPSArray("aaaaa"));
+
+ // Test case 4: "abcabcabc" -> [0, 0, 0, 1, 2, 3, 4, 5, 6]
+ Assertions.assertArrayEquals(new int[]{0, 0, 0, 1, 2, 3, 4, 5, 6}, Main.computeLPSArray("abcabcabc"));
+
+ // Test case 5: "aabaacaadaa" -> [0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2] (Corrected)
+ Assertions.assertArrayEquals(new int[]{0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2}, Main.computeLPSArray("aabaacaadaa"));
+
+ // Test case 6: Empty string -> []
+ Assertions.assertArrayEquals(new int[]{}, Main.computeLPSArray(""));
+ }
+
+
+ @Test
+ public void testKmpScenarios() {
+ // 1. No match
+ Assertions.assertEquals(0, Main.kmpSearch("abcde", "xyz"));
+
+ // 2. Simple match
+ Assertions.assertEquals(1, Main.kmpSearch("abcde", "bcd"));
+
+ // 3. Multiple matches
+ Assertions.assertEquals(3, Main.kmpSearch("abababa", "aba"));
+
+ // 4. Overlapping matches
+ Assertions.assertEquals(4, Main.kmpSearch("aaaaa", "aa"));
+
+ // 5. Pattern at the beginning
+ Assertions.assertEquals(1, Main.kmpSearch("abcde", "ab"));
+
+ // 6. Pattern at the end
+ Assertions.assertEquals(1, Main.kmpSearch("abcde", "de"));
+
+ // 7. Text and pattern are identical
+ Assertions.assertEquals(1, Main.kmpSearch("abcde", "abcde"));
+
+ // 8. Empty text
+ Assertions.assertEquals(0, Main.kmpSearch("", "a"));
+
+ // 9. Empty pattern (as per implementation, returns 0)
+ Assertions.assertEquals(0, Main.kmpSearch("abcde", ""));
+
+ // 10. Pattern is longer than text
+ Assertions.assertEquals(0, Main.kmpSearch("abc", "abcd"));
+
+ // 11. Complex case from sample
+ Assertions.assertEquals(3, Main.kmpSearch("chenljnbwowowoo", "wo"));
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
index 1ff9de0..577c9ee 100644
--- a/2018fall/lab_5/pom.xml
+++ b/2018fall/lab_5/pom.xml
@@ -19,6 +19,7 @@
lab_5_1145
lab_5_1146
+ lab_5_1047
From ce9762828fb1359e2391379045a7eaeb7d5158df Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 05:51:34 +0000
Subject: [PATCH 12/46] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=202018fall/lab?=
=?UTF-8?q?5-1148?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/README.md | 4 +-
2018fall/lab_5/lab_5_1148/README.md | 100 ++++++++++
2018fall/lab_5/lab_5_1148/pom.xml | 22 +++
.../lab_5/lab_5_1148/resources/01.data.in | 5 +
.../lab_5/lab_5_1148/resources/01.data.out | 2 +
.../lab_5/lab_5_1148/resources/02.data.in | 7 +
.../lab_5/lab_5_1148/resources/02.data.out | 3 +
2018fall/lab_5/lab_5_1148/src/Main.java | 176 ++++++++++++++++++
2018fall/lab_5/lab_5_1148/test/MainTest.java | 132 +++++++++++++
2018fall/lab_5/pom.xml | 1 +
2018fall/lab_5/submit.csv | 6 +-
2018fall/AGENTS.md => AGENTS.md | 17 +-
12 files changed, 467 insertions(+), 8 deletions(-)
create mode 100644 2018fall/lab_5/lab_5_1148/README.md
create mode 100644 2018fall/lab_5/lab_5_1148/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1148/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1148/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1148/resources/02.data.in
create mode 100644 2018fall/lab_5/lab_5_1148/resources/02.data.out
create mode 100644 2018fall/lab_5/lab_5_1148/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1148/test/MainTest.java
rename 2018fall/AGENTS.md => AGENTS.md (59%)
diff --git a/2018fall/lab_5/README.md b/2018fall/lab_5/README.md
index c3153ef..321bab6 100644
--- a/2018fall/lab_5/README.md
+++ b/2018fall/lab_5/README.md
@@ -19,8 +19,8 @@ Read the samples carefully can help you understand the problem.
## Stack And Queue
+ [x] problem A: lab_5_1145
-+ [ ] problem B: lab_5_1146
-+ [ ] problem C: lab_5_1047
++ [x] problem B: lab_5_1146
++ [x] problem C: lab_5_1047
+ [ ] problem D: lab_5_1148
+ [ ] problem E: lab_5_1149
+ [ ] problem F: lab_5_1150
diff --git a/2018fall/lab_5/lab_5_1148/README.md b/2018fall/lab_5/lab_5_1148/README.md
new file mode 100644
index 0000000..7e2c074
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/README.md
@@ -0,0 +1,100 @@
+## Description
+
+Hong has learned something about music. He finds that punchline is very important.
+
+If a substring appears 3 times at the beginning, the middle, and the end of a lyric, the substring is a punchline.
+
+But it’s hard for Hong to find a punchline. He wants you to help him to find the longest punchline in a song.
+
+There is no overlap among the substrings.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 100), which is the number of test cases.
+
+For each test data:
+
+The first line contains an integer n (1 <= n <= 10^5) — the length of the string s.
+
+The second line is the lyric containing string s of length n, which consists of lowercase letters only.
+
+### Output
+
+For each case, please print the length of the longest punchline.
+
+### Sample Input
+
+```log
+2
+6
+ababab
+7
+abababa
+```
+
+### Sample Output
+
+```log
+2
+1
+```
+
+> 这里case2输出不是3, 而是1, 因为aba是 {0,1,2}, {2,3,4}, {4,5,6} 三段, 有重叠
+
+## 解答(AC 版本:lps 边界回退 + Z-Algorithm 严格判定)
+
+目标:找到最长子串 x,使其同时满足
+- 是 s 的前缀;
+- 是 s 的后缀;
+- 在中间再出现一次;
+- 三次出现两两不重叠(no overlap)。
+
+核心分两步:
+1) 用 KMP 的前缀函数 lps 找到所有“边界”(同时为前缀和后缀的子串长度),并按“越长越优”的顺序尝试。
+2) 对每个候选长度 len,用 Z-Algorithm 严格判断“中间是否存在一段与前缀相等的子串”且不与前缀/后缀重叠。
+
+记 n = |s|。
+- lps[i] 表示 s[0..i] 的最长真前后缀长度,因此最长边界为 L0 = lps[n-1],次长边界为 lps[L0-1],以此类推。
+- Z 数组 z[j] 表示 s[j..] 与 s[0..] 的最长公共前缀长度。
+
+不重叠约束的区间化:
+- 若答案长度为 len,则三段区间为
+ - 前缀:[0, len-1]
+ - 中间:[j, j+len-1]
+ - 后缀:[n-len, n-1]
+- 为不重叠,需满足 j ≥ len 且 j+len-1 ≤ n-len-1,即中间“起点” j ∈ [len, n-2*len]。
+- 用 Z 判断:存在 j ∈ [len, n-2*len] 使得 z[j] ≥ len,则中间合法出现一次。
+
+算法流程:
+1) 计算整串 s 的 lps 与 z;
+2) 从 len = lps[n-1] 开始,循环:
+ - 若 len == 0,则无解(返回 0)。
+ - 令区间 J = [len, n-2*len],若 J 非空且 max(z[j] | j∈J) ≥ len,则返回 len;
+ - 否则 len = lps[len-1](退到更短的边界),继续判断,直到找到或为 0。
+
+实现加速:
+- 为了 O(1) 查询任意区间 J 的 max(z[j]),可对 z 构建稀疏表(RMQ)。整体复杂度 O(n log n),常数小且足够通过;若更偏向简洁实现,也可直接线性扫描 J(配合边界回退,通常也能过)。
+
+正确性要点:
+- 边界保证了“前缀=后缀”;
+- Z[j] ≥ len 保证了“中间的长度为 len 的子串等于前缀”;
+- j 的取值范围 [len, n-2*len] 保证三段不重叠;
+- 从最长边界往下回退,首次命中的即为“尽可能长”的答案。
+
+样例解释:
+- s = "ababab":取 len=2 的 "ab",三段分别为 [0..1]、[2..3]、[4..5],互不重叠,答案 2。
+- s = "abababa":
+ - "aba" 虽出现三次,但两两重叠,不合法;
+ - "ab" 不是后缀;
+ - 只能取 "a",三段 [0..0]、[2..2]、[6..6] 不重叠,答案 1。
+
+常见坑点:
+- 忽略“中间段不重叠”的位置约束,导致把重叠的三次出现也算进来(如把 "aba" 判为合法);
+- 只回退一次边界,未沿 lps 链持续回退;
+- 把“是否存在区间内 ≥ len”写成前缀最大值的启发式,可能误判。Z+区间最大查询是更稳妥的写法。
+
+复杂度:
+- 计算 lps 与 z 均为 O(n);
+- RMQ 预处理 O(n log n),查询 O(1);
+- 沿边界链回退总步数 O(n);
+- 整体 O(n log n),空间 O(n log n)。若改为线性扫描区间 J,可降为 O(n) 代码体量更小(但最坏常数略大)。
diff --git a/2018fall/lab_5/lab_5_1148/pom.xml b/2018fall/lab_5/lab_5_1148/pom.xml
new file mode 100644
index 0000000..81f8681
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1148
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1148/resources/01.data.in b/2018fall/lab_5/lab_5_1148/resources/01.data.in
new file mode 100644
index 0000000..6b2032c
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/resources/01.data.in
@@ -0,0 +1,5 @@
+2
+6
+ababab
+7
+abababa
diff --git a/2018fall/lab_5/lab_5_1148/resources/01.data.out b/2018fall/lab_5/lab_5_1148/resources/01.data.out
new file mode 100644
index 0000000..5f1d0ec
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/resources/01.data.out
@@ -0,0 +1,2 @@
+2
+1
diff --git a/2018fall/lab_5/lab_5_1148/resources/02.data.in b/2018fall/lab_5/lab_5_1148/resources/02.data.in
new file mode 100644
index 0000000..2288873
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/resources/02.data.in
@@ -0,0 +1,7 @@
+3
+5
+aaaaa
+5
+abcde
+7
+abacaba
diff --git a/2018fall/lab_5/lab_5_1148/resources/02.data.out b/2018fall/lab_5/lab_5_1148/resources/02.data.out
new file mode 100644
index 0000000..16db301
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/resources/02.data.out
@@ -0,0 +1,3 @@
+1
+0
+1
diff --git a/2018fall/lab_5/lab_5_1148/src/Main.java b/2018fall/lab_5/lab_5_1148/src/Main.java
new file mode 100644
index 0000000..b6fc1f5
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/src/Main.java
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ final String s;
+
+ public TestCase(String s) {
+ this.s = s;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 100) : "T must be between 1 and 100";
+ final List cases = new ArrayList<>(testCases);
+ for (int t = 0; t < testCases; t++) {
+ final int n = in.nextInt();
+ final String s = in.next();
+ assert s.length() == n : "String length should be n";
+ assert n >= 1 && n <= 100000 : "n must be between 1 and 10^5";
+ cases.add(new TestCase(s));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ results.add(solve(tc.s));
+ }
+ return results;
+ }
+
+ private static int solve(String s) {
+ final int n = s.length();
+ if (n < 3) {
+ return 0;
+ }
+
+ final int[] lps = computeLPSArray(s);
+ final int[] z = computeZArray(s);
+ final RMQ rmq = new RMQ(z);
+
+ int len = lps[n - 1]; // 从最长边界开始
+ while (len > 0) {
+ // 中间那次出现的起点 j 范围: [len, n - 2*len]
+ final int L = len;
+ final int R = n - 2 * len;
+ if (L <= R) {
+ int maxZ = rmq.queryMax(L, R);
+ if (maxZ >= len) {
+ return len; // 找到满足条件的最长答案
+ }
+ }
+ len = lps[len - 1]; // 退到次长边界
+ }
+ return 0;
+ }
+
+ // Z-Algorithm: z[i] = LCP(s[i..], s[0..])
+ private static int[] computeZArray(String s) {
+ final int n = s.length();
+ final int[] z = new int[n];
+ z[0] = n;
+ int l = 0, r = 0;
+ for (int i = 1; i < n; i++) {
+ if (i <= r) {
+ z[i] = Math.min(r - i + 1, z[i - l]);
+ }
+ while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) {
+ z[i]++;
+ }
+ if (i + z[i] - 1 > r) {
+ l = i;
+ r = i + z[i] - 1;
+ }
+ }
+ return z;
+ }
+
+ // 稀疏表 RMQ (Range Max Query) for Z-array
+ private static final class RMQ {
+ private final int[][] st;
+ private final int[] lg;
+
+ RMQ(int[] a) {
+ int n = a.length;
+ lg = new int[n + 1];
+ for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
+ int K = lg[n] + 1;
+ st = new int[K][n];
+ System.arraycopy(a, 0, st[0], 0, n);
+ for (int k = 1; k < K; k++) {
+ int len = 1 << k;
+ int half = len >> 1;
+ for (int i = 0; i + len <= n; i++) {
+ st[k][i] = Math.max(st[k - 1][i], st[k - 1][i + half]);
+ }
+ }
+ }
+
+ int queryMax(int l, int r) {
+ if (l > r) return 0;
+ int k = lg[r - l + 1];
+ return Math.max(st[k][l], st[k][r - (1 << k) + 1]);
+ }
+ }
+
+ public static int[] computeLPSArray(String pattern) {
+ final int m = pattern.length();
+ final int[] lps = new int[m];
+ if (m == 0) {
+ return lps;
+ }
+ for (int length = 0, i = 1; i < m; ) {
+ if (pattern.charAt(i) == pattern.charAt(length)) {
+ length++;
+ lps[i] = length;
+ i++;
+ } else {
+ if (length != 0) {
+ length = lps[length - 1];
+ } else {
+ lps[i] = 0;
+ i++;
+ }
+ }
+ }
+ return lps;
+ }
+
+ public static void output(List decides) {
+ for (final var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1148/test/MainTest.java b/2018fall/lab_5/lab_5_1148/test/MainTest.java
new file mode 100644
index 0000000..99022b7
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1148/test/MainTest.java
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_1() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "01.data.in", "01.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "02.data.in", "02.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("02.data.out", "02.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+ @Test
+ void testCalSampleCases() {
+ final List inputs = new ArrayList<>();
+ inputs.add(new Main.TestCase("ababab"));
+ inputs.add(new Main.TestCase("abababa"));
+
+ final List expected = List.of(2, 1);
+ final List actual = Main.cal(inputs);
+
+ Assertions.assertEquals(expected, actual);
+ }
+
+ @Test
+ void testNLessThan3() {
+ final List inputs = List.of(
+ new Main.TestCase("a"),
+ new Main.TestCase("ab")
+ );
+ final List expected = List.of(0, 0);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+
+ @Test
+ void testNoPunchline() {
+ final List inputs = List.of(
+ new Main.TestCase("abcde")
+ );
+ final List expected = List.of(0);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+
+ @Test
+ void testOverlapRequiresShorterCandidate() {
+ final List inputs = List.of(
+ new Main.TestCase("aaaaa")
+ );
+ final List expected = List.of(1);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+
+ @Test
+ void testLongestPossibleNonOverlapping() {
+ final List inputs = List.of(
+ new Main.TestCase("abcabcabc")
+ );
+ final List expected = List.of(3);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+
+ @Test
+ void testComplexOverlap() {
+ final List inputs = List.of(
+ new Main.TestCase("abacabacabac")
+ );
+ final List expected = List.of(4);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+
+ @Test
+ void testNoMiddleOccurrence() {
+ final List inputs = List.of(
+ new Main.TestCase("abccba")
+ );
+ final List expected = List.of(0);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+
+ @Test
+ void testLongStringWithLongNextChain() {
+ final List inputs = List.of(
+ new Main.TestCase("ababababab")
+ );
+ final List expected = List.of(2);
+ Assertions.assertEquals(expected, Main.cal(inputs));
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
index 577c9ee..312480c 100644
--- a/2018fall/lab_5/pom.xml
+++ b/2018fall/lab_5/pom.xml
@@ -20,6 +20,7 @@
lab_5_1145
lab_5_1146
lab_5_1047
+ lab_5_1148
diff --git a/2018fall/lab_5/submit.csv b/2018fall/lab_5/submit.csv
index df03337..cf59975 100644
--- a/2018fall/lab_5/submit.csv
+++ b/2018fall/lab_5/submit.csv
@@ -1,9 +1,9 @@
AC, PE, WA, TLE, MLE, OLE, RE, CE, TR, Total , C, C++, Java
A, 207, 217, 2, 34, 26, 145, 631, 117, 109, 405
B, 57, 1, 499, 2, 12, 2, 310, 44, 308, 1235, 74, 245, 916
-G, 46, 3, 206, 128, 9, 50, 20, 92, 554, 82, 94, 378
+C, 228, 351, 311, 11, 3, 100, 73, 357, 1434, 134, 184, 1116
+D, 179, 618, 216, 1, 6, 151, 62, 810, 2043, 181, 312, 1550
E, 210, 5, 355, 134, 2, 52, 77, 15, 345, 1195, 81, 333, 781
F, 167, 306, 152, 4, 69, 28, 391, 1117, 30, 213, 874
-D, 179, 618, 216, 1, 6, 151, 62, 810, 2043, 181, 312, 1550
-C, 228, 351, 311, 11, 3, 100, 73, 357, 1434, 134, 184, 1116
+G, 46, 3, 206, 128, 9, 50, 20, 92, 554, 82, 94, 378
Total, 1094, 9, 2552, 943, 26, 78, 791, 268, 2448, 8209, 699, 1490, 6020
diff --git a/2018fall/AGENTS.md b/AGENTS.md
similarity index 59%
rename from 2018fall/AGENTS.md
rename to AGENTS.md
index d7c2dd8..d5d7d7d 100644
--- a/2018fall/AGENTS.md
+++ b/AGENTS.md
@@ -2,8 +2,7 @@
1. 使用 JDK11 语法, 尽量使用现代数据结构, 尽量使用final var不可变变量
2. 尽量遵守读-处理-输出分离的原则
-3. 尽量使用 `System.out.print('\n')` 来表示换行
-4. 不使用任何中文标点
+3. 不使用任何中文标点
+ 以使用 `"` 为荣, 以使用 `“`, `”` 为耻
+ 以使用 `'` 为荣, 以使用 `‘`, `’` 为耻
@@ -16,6 +15,8 @@
> 注意给英文字符留出一个空格的空白
+4. 禁止使用 `**` 加强符号
+
## 定义操作
1. 定义对README进行的预处理
@@ -27,4 +28,14 @@
2. 定义解答流程
+ 根据题目描述, 以及输入输出文件 data.in, data.out, 按照JDK11语法, 并遵守读-处理-输出分离的原则, 重写 Main.java
-+ 使用题目约束, 在 Main.java 的 reader 内部加入 assert 判断
+ + 使用默认的快读类
+ + 读取方法 reader 使用快读类, 将读取数据抽象为类, 并传递到处理函数cal
+ + 处理函数 cal 内部处理, 并将结果传递给输出函数 output
+ + 输出函数 output 接受结果, 尽可能地优化输出
+ + 注意不要使用 `if (i < results.size() - 1) { System.out.print('\n'); } ` 这种方式, 最后一个也要输出换行
+ + 使用 `System.out.print('\n')` 来表示换行
+ + 不需要使用 `java.io.PrintWriter`
++ 使用题目约束, 在 Main.java 的 reader 内部加入 assert 判断, 并尽量对每一个 assert 判断中的 case 添加括号
+ + example: `assert ((0 <= x) && (x <= 100));`
+
+3. 执行测试的命令行操作: `mvn -q -pl .\2018fall\lab_{}\lab_{}_{}\ -am test`
From 1b4c1bf52559801d636d67b6cd24c711651e012d Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 06:07:11 +0000
Subject: [PATCH 13/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?5-1149?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/README.md | 4 +-
2018fall/lab_5/lab_5_1148/README.md | 2 +-
2018fall/lab_5/lab_5_1149/README.md | 61 +++++++++
2018fall/lab_5/lab_5_1149/pom.xml | 22 ++++
.../lab_5/lab_5_1149/resources/01.data.in | 4 +
.../lab_5/lab_5_1149/resources/01.data.out | 1 +
2018fall/lab_5/lab_5_1149/src/Main.java | 119 ++++++++++++++++++
2018fall/lab_5/lab_5_1149/test/MainTest.java | 46 +++++++
2018fall/lab_5/pom.xml | 1 +
9 files changed, 257 insertions(+), 3 deletions(-)
create mode 100644 2018fall/lab_5/lab_5_1149/README.md
create mode 100644 2018fall/lab_5/lab_5_1149/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1149/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1149/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1149/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1149/test/MainTest.java
diff --git a/2018fall/lab_5/README.md b/2018fall/lab_5/README.md
index 321bab6..37e5a25 100644
--- a/2018fall/lab_5/README.md
+++ b/2018fall/lab_5/README.md
@@ -21,8 +21,8 @@ Read the samples carefully can help you understand the problem.
+ [x] problem A: lab_5_1145
+ [x] problem B: lab_5_1146
+ [x] problem C: lab_5_1047
-+ [ ] problem D: lab_5_1148
-+ [ ] problem E: lab_5_1149
++ [x] problem D: lab_5_1148
++ [x] problem E: lab_5_1149
+ [ ] problem F: lab_5_1150
+ [ ] problem G: lab_5_1151
diff --git a/2018fall/lab_5/lab_5_1148/README.md b/2018fall/lab_5/lab_5_1148/README.md
index 7e2c074..14a5f3d 100644
--- a/2018fall/lab_5/lab_5_1148/README.md
+++ b/2018fall/lab_5/lab_5_1148/README.md
@@ -4,7 +4,7 @@ Hong has learned something about music. He finds that punchline is very importan
If a substring appears 3 times at the beginning, the middle, and the end of a lyric, the substring is a punchline.
-But it’s hard for Hong to find a punchline. He wants you to help him to find the longest punchline in a song.
+But it’s hard for Hong to find a punchline. He wants you to help him find the longest punchline in a song.
There is no overlap among the substrings.
diff --git a/2018fall/lab_5/lab_5_1149/README.md b/2018fall/lab_5/lab_5_1149/README.md
new file mode 100644
index 0000000..b9636c7
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1149/README.md
@@ -0,0 +1,61 @@
+## Description
+
+Hong has two strings S and T, finds the longest prefix of S that is a suffix of T.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 50), which is the number of test cases.
+
+For each test data:
+
+The first line contains two integers n and m (1 <= n, m <= 10^5) meaning the length of the string S and the length of the string T, respectively.
+
+The second line contains string s of length n, which consists only of lowercase letters.
+
+The third line contains string t of length m, which consists only of lowercase letters.
+
+### Output
+
+For each case, please print the length of the longest prefix of S that is a suffix of T and the corresponding prefix.
+
+### Sample Input
+
+```log
+1
+3 5
+abc
+bcbab
+```
+
+### Sample Output
+
+```log
+2 ab
+```
+
+## 解答
+
+本题要求我们找到字符串 `S` 的一个最长前缀,这个前缀同时也是字符串 `T` 的一个后缀。
+
+这是一个经典的字符串匹配问题,可以通过巧妙地运用 **KMP 算法的前缀函数(lps 数组)** 来高效解决。
+
+### 算法思路
+
+1. **构造新字符串**:我们将 `S` 和 `T` 通过一个不会在原字符串中出现的特殊字符(例如 `#`)连接起来,形成一个新的字符串 `combined = S + '#' + T`。
+ * 以示例输入为例:`S = "abc"`, `T = "bcbab"`,那么 `combined` 就是 `"abc#bcbab"`。
+
+2. **计算 lps 数组**:我们为这个 `combined` 字符串计算其 `lps` 数组。`lps` 数组的定义是:`lps[i]` 表示子串 `combined[0...i]` 的最长公共真前后缀(Longest Proper Prefix which is also Suffix)的长度。
+
+3. **获取答案**:`lps` 数组的**最后一个值**,即 `lps[combined.length() - 1]`,就是我们要求的答案长度。
+ * **为什么?** 因为 `#` 是一个独特的字符,它保证了 `combined` 字符串的任何公共前后缀都不可能跨越 `#`。因此,`combined` 的最长公共前后缀,必然是 `S` 的一个前缀,并且同时是 `T` 的一个后缀。这恰好就是题目所求。
+
+### 示例演练
+
+- `S = "abc"`, `T = "bcbab"`
+- `combined = "abc#bcbab"`
+- 计算 `combined` 的 `lps` 数组,其最后一个值为 `2`。
+ * 这是因为 `combined` 的前缀 `"ab"` 与其后缀 `"ab"` 相等,是其最长的公共前后缀。
+- 因此,最长长度为 `2`。我们从 `S` 中截取长度为 `2` 的前缀,即 `"ab"`。
+- 最终输出 `2 ab`。
+
+这种方法将问题转化为了一个标准的 `lps` 数组计算,算法复杂度为 O(|S| + |T|),非常高效。
diff --git a/2018fall/lab_5/lab_5_1149/pom.xml b/2018fall/lab_5/lab_5_1149/pom.xml
new file mode 100644
index 0000000..761e643
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1149/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1149
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1149/resources/01.data.in b/2018fall/lab_5/lab_5_1149/resources/01.data.in
new file mode 100644
index 0000000..9b9c727
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1149/resources/01.data.in
@@ -0,0 +1,4 @@
+1
+3 5
+abc
+bcbab
diff --git a/2018fall/lab_5/lab_5_1149/resources/01.data.out b/2018fall/lab_5/lab_5_1149/resources/01.data.out
new file mode 100644
index 0000000..ee39f16
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1149/resources/01.data.out
@@ -0,0 +1 @@
+2 ab
diff --git a/2018fall/lab_5/lab_5_1149/src/Main.java b/2018fall/lab_5/lab_5_1149/src/Main.java
new file mode 100644
index 0000000..79b2d0b
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1149/src/Main.java
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ final String s;
+ final String t;
+
+ public TestCase(String s, String t) {
+ this.s = s;
+ this.t = t;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 50) : "T must be between 1 and 50";
+ final List cases = new ArrayList<>(testCases);
+ for (int i = 0; i < testCases; i++) {
+ final int n = in.nextInt();
+ final int m = in.nextInt();
+ final String s = in.next();
+ final String t = in.next();
+ assert s.length() == n : "s length should be n";
+ assert t.length() == m : "t length should be m";
+ assert n >= 1 && n <= 100000 : "n must be between 1 and 10^5";
+ assert m >= 1 && m <= 100000 : "m must be between 1 and 10^5";
+ cases.add(new TestCase(s, t));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ results.add(solve(tc.s, tc.t));
+ }
+ return results;
+ }
+
+ private static String solve(String s, String t) {
+ final String combined = s + '#' + t;
+ final int[] lps = computeLPSArray(combined);
+ final int longestLength = lps[lps.length - 1];
+
+ if (longestLength == 0) {
+ return "0";
+ } else {
+ return longestLength + " " + s.substring(0, longestLength);
+ }
+ }
+
+ private static int[] computeLPSArray(String pattern) {
+ final int m = pattern.length();
+ final int[] lps = new int[m];
+ if (m == 0) {
+ return lps;
+ }
+ for (int length = 0, i = 1; i < m; ) {
+ if (pattern.charAt(i) == pattern.charAt(length)) {
+ length++;
+ lps[i] = length;
+ i++;
+ } else {
+ if (length != 0) {
+ length = lps[length - 1];
+ } else {
+ lps[i] = 0;
+ i++;
+ }
+ }
+ }
+ return lps;
+ }
+
+ public static void output(List decides) {
+ for (final var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1149/test/MainTest.java b/2018fall/lab_5/lab_5_1149/test/MainTest.java
new file mode 100644
index 0000000..69280ba
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1149/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_1() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "01.data.in", "01.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
index 312480c..e2a5d6e 100644
--- a/2018fall/lab_5/pom.xml
+++ b/2018fall/lab_5/pom.xml
@@ -21,6 +21,7 @@
lab_5_1146
lab_5_1047
lab_5_1148
+ lab_5_1149
From 24e6a6153096f916d0b3fa8b2d470b114e297ec9 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 06:16:23 +0000
Subject: [PATCH 14/46] feat: add 2018fall/lab5-1150
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/lab_5_1150/README.md | 65 ++++++++
2018fall/lab_5/lab_5_1150/pom.xml | 22 +++
.../lab_5/lab_5_1150/resources/01.data.in | 5 +
.../lab_5/lab_5_1150/resources/01.data.out | 1 +
2018fall/lab_5/lab_5_1150/src/Main.java | 148 ++++++++++++++++++
2018fall/lab_5/lab_5_1150/test/MainTest.java | 46 ++++++
2018fall/lab_5/pom.xml | 1 +
7 files changed, 288 insertions(+)
create mode 100644 2018fall/lab_5/lab_5_1150/README.md
create mode 100644 2018fall/lab_5/lab_5_1150/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1150/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1150/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1150/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1150/test/MainTest.java
diff --git a/2018fall/lab_5/lab_5_1150/README.md b/2018fall/lab_5/lab_5_1150/README.md
new file mode 100644
index 0000000..2499019
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1150/README.md
@@ -0,0 +1,65 @@
+## Description
+
+It's hard for Hong to find the longest common substring of N different strings.
+
+Hong wants you to solve this question.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 10), which is the number of test cases.
+
+For each test data:
+
+The first line contains an integer N (1 <= N <= 1000) — the number of the sentences.
+
+Each of the next N lines contains a string s, which consists of lowercase letters (no space) only. The length of each string will be at least 1 and at most 200 characters.
+
+### Output
+
+For each case please print the lexicographically smallest longest common substring.
+
+If there is no such non-empty string, output the words “Hong” instead.
+
+### Sample Input
+
+```log
+1
+3
+aabbaabb
+abbababb
+bbbbbabb
+```
+
+### Sample Output
+
+```log
+abb
+```
+
+## 解答
+
+本题要求我们找到 N 个字符串的“最长公共子串”,并且在所有最长公共子串中,返回字典序最小的那一个。
+
+考虑到字符串数量和长度的限制(N <= 1000, |s| <= 200),一个清晰且有效的解法是 二分答案 + 暴力验证。
+
+### 算法思路
+
+1. 二分答案 (Binary Search on Answer)
+ 我们不对子串本身进行搜索,而是对“最长公共子串的长度”进行二分查找。这个长度 `L` 的范围是 `[0, min_len]`,其中 `min_len` 是所有输入字符串中最短的那个的长度。
+ - 对于二分出的每一个长度 `mid`,我们都需要一个 `check(mid)` 函数来回答:“是否存在一个长度为 `mid` 的公共子串?”
+ - 如果 `check(mid)` 返回 `true`,说明长度 `mid` 是可行的,我们尝试寻找更长的公共子串,因此 `low = mid + 1`。
+ - 如果 `check(mid)` 返回 `false`,说明 `mid` 太长了,我们需要缩短长度,因此 `high = mid - 1`。
+
+2. `check(len)` 验证函数
+ 这个函数负责验证是否存在一个长度为 `len` 的子串,它同时出现在所有 N 个字符串中。
+ - 生成候选集:我们从第一个字符串 `s_1` 中提取出所有长度为 `len` 的子串,并将它们放入一个 `Set`(集合)中,作为初始的“候选公共子串集”。
+ - 迭代求交集:遍历剩下的字符串 `s_2, s_3, ..., s_N`。对于每一个字符串 `s_i`,我们都对候选集进行筛选,只保留那些也出现在 `s_i` 中的子串。
+ - 剪枝:如果在任何一步筛选后,候选集变为空,那么我们就可以确定不存在长度为 `len` 的公共子串,`check` 函数立即返回失败。
+ - 成功条件:如果成功遍历完所有 N 个字符串,候选集依然不为空,那么 `check` 函数返回成功,并将最终的候选集(即所有长度为 `len` 的公共子串)返回。
+
+3. 寻找最终答案
+ - 通过二分查找,我们可以找到满足 `check(L)` 的最大长度,记为 `maxL`。
+ - 如果 `maxL` 为 0,说明不存在任何非空公共子串,按题目要求输出 "Hong"。
+ - 否则,我们再次调用 `check(maxL)` 来获取所有长度为 `maxL` 的公共子串。然后对这些子串进行字典序排序,并返回第一个(即最小的)作为最终答案。
+
+`Main.java` 中的 `solve` 和 `check` 方法正是遵循了这一逻辑。虽然 `check` 函数内部的 `String.contains()` 效率不是最优,但鉴于本题的数据规模,这种清晰的实现足以通过评测。
diff --git a/2018fall/lab_5/lab_5_1150/pom.xml b/2018fall/lab_5/lab_5_1150/pom.xml
new file mode 100644
index 0000000..7c076ff
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1150/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1150
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1150/resources/01.data.in b/2018fall/lab_5/lab_5_1150/resources/01.data.in
new file mode 100644
index 0000000..ddf40f7
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1150/resources/01.data.in
@@ -0,0 +1,5 @@
+1
+3
+aabbaabb
+abbababb
+bbbbbabb
diff --git a/2018fall/lab_5/lab_5_1150/resources/01.data.out b/2018fall/lab_5/lab_5_1150/resources/01.data.out
new file mode 100644
index 0000000..e86bced
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1150/resources/01.data.out
@@ -0,0 +1 @@
+abb
diff --git a/2018fall/lab_5/lab_5_1150/src/Main.java b/2018fall/lab_5/lab_5_1150/src/Main.java
new file mode 100644
index 0000000..20ebe09
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1150/src/Main.java
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ final List strings;
+
+ public TestCase(List strings) {
+ this.strings = strings;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 10) : "T must be between 1 and 10";
+ final List cases = new ArrayList<>(testCases);
+ for (int i = 0; i < testCases; i++) {
+ final int n = in.nextInt();
+ assert (n >= 1) && (n <= 1000) : "N must be between 1 and 1000";
+ final List stringList = new ArrayList<>(n);
+ for (int j = 0; j < n; j++) {
+ String s = in.next();
+ assert (s.length() >= 1) && (s.length() <= 200) : "String length must be between 1 and 200";
+ stringList.add(s);
+ }
+ cases.add(new TestCase(stringList));
+ }
+ return cases;
+ }
+
+ public static List cal(List inputs) {
+ final List results = new ArrayList<>();
+ for (final var tc : inputs) {
+ results.add(solve(tc.strings));
+ }
+ return results;
+ }
+ private static final String HONG = "Hong";
+
+ private static String solve(List strings) {
+ if (strings == null || strings.isEmpty()) {
+ return HONG;
+ }
+ int minLen = strings.get(0).length();
+ for (String s : strings) {
+ minLen = Math.min(minLen, s.length());
+ }
+
+ int low = 0, high = minLen;
+ int maxL = 0;
+
+ while (low <= high) {
+ int mid = low + (high - low) / 2;
+ if (mid == 0) {
+ low = mid + 1;
+ continue;
+ }
+ if (check(mid, strings) != null) {
+ maxL = mid;
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+
+ if (maxL == 0) {
+ return HONG;
+ }
+
+ // Find the lexicographically smallest one of length maxL
+ List candidates = new ArrayList<>(check(maxL, strings));
+ Collections.sort(candidates);
+ return candidates.get(0);
+ }
+
+ private static Set check(int len, List strings) {
+ String firstString = strings.get(0);
+ Set commonSubstrings = new HashSet<>();
+
+ for (int i = 0; i <= firstString.length() - len; i++) {
+ commonSubstrings.add(firstString.substring(i, i + len));
+ }
+
+ for (int i = 1; i < strings.size(); i++) {
+ String currentString = strings.get(i);
+ Set nextCommonSubstrings = new HashSet<>();
+ for (String sub : commonSubstrings) {
+ if (currentString.contains(sub)) {
+ nextCommonSubstrings.add(sub);
+ }
+ }
+ commonSubstrings = nextCommonSubstrings;
+ if (commonSubstrings.isEmpty()) {
+ return null;
+ }
+ }
+
+ return commonSubstrings.isEmpty() ? null : commonSubstrings;
+ }
+
+
+ public static void output(List decides) {
+ for (final var decide : decides) {
+ System.out.print(decide);
+ System.out.print('\n');
+ }
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1150/test/MainTest.java b/2018fall/lab_5/lab_5_1150/test/MainTest.java
new file mode 100644
index 0000000..69280ba
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1150/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_1() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "01.data.in", "01.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
index e2a5d6e..d88cdc9 100644
--- a/2018fall/lab_5/pom.xml
+++ b/2018fall/lab_5/pom.xml
@@ -22,6 +22,7 @@
lab_5_1047
lab_5_1148
lab_5_1149
+ lab_5_1150
From 26901af58dec5f5d55d47f4518d5fbd2b90fe203 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 07:19:04 +0000
Subject: [PATCH 15/46] feat: add 2018fall/lab5-1151
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/lab_5_1151/README.md | 91 +++++++++
2018fall/lab_5/lab_5_1151/pom.xml | 22 +++
.../lab_5/lab_5_1151/resources/01.data.in | 4 +
.../lab_5/lab_5_1151/resources/01.data.out | 5 +
.../lab_5/lab_5_1151/resources/02.data.in | 4 +
.../lab_5/lab_5_1151/resources/02.data.out | 2 +
.../lab_5/lab_5_1151/resources/03.data.in | 4 +
.../lab_5/lab_5_1151/resources/03.data.out | 4 +
.../lab_5/lab_5_1151/resources/04.data.in | 5 +
.../lab_5/lab_5_1151/resources/04.data.out | 5 +
2018fall/lab_5/lab_5_1151/src/Main.java | 185 ++++++++++++++++++
2018fall/lab_5/lab_5_1151/test/MainTest.java | 74 +++++++
2018fall/lab_5/pom.xml | 1 +
13 files changed, 406 insertions(+)
create mode 100644 2018fall/lab_5/lab_5_1151/README.md
create mode 100644 2018fall/lab_5/lab_5_1151/pom.xml
create mode 100644 2018fall/lab_5/lab_5_1151/resources/01.data.in
create mode 100644 2018fall/lab_5/lab_5_1151/resources/01.data.out
create mode 100644 2018fall/lab_5/lab_5_1151/resources/02.data.in
create mode 100644 2018fall/lab_5/lab_5_1151/resources/02.data.out
create mode 100644 2018fall/lab_5/lab_5_1151/resources/03.data.in
create mode 100644 2018fall/lab_5/lab_5_1151/resources/03.data.out
create mode 100644 2018fall/lab_5/lab_5_1151/resources/04.data.in
create mode 100644 2018fall/lab_5/lab_5_1151/resources/04.data.out
create mode 100644 2018fall/lab_5/lab_5_1151/src/Main.java
create mode 100644 2018fall/lab_5/lab_5_1151/test/MainTest.java
diff --git a/2018fall/lab_5/lab_5_1151/README.md b/2018fall/lab_5/lab_5_1151/README.md
new file mode 100644
index 0000000..6019dc0
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/README.md
@@ -0,0 +1,91 @@
+## Description
+
+Given n words, Hong wants to input one of these words. He wants to input (at the end) as few characters (without backspace) as possible, to make at least one of the n words appears (as a suffix) in the text.
+
+Given an operation sequence, Hong wants to know the answer after every operation.
+
+An operation might input a character or delete the last character.
+
+### Input
+
+The first line contains one integer n.
+
+In the following n lines, each line contains a word.
+
+The last line contains the operation sequence.
+
+'-' means backspace and will delete the last character he typed.
+
+He may backspace when there are no characters left, and nothing will happen.
+
+- 1 <= n <= 4
+- The total length of n words <= 100000
+- The length of the operation sequence <= 100000
+- The words and the sequence only contain lower case letter.
+
+### Output
+
+You should output L + 1 integers, where L is the length of the operation sequence.
+
+The i-th(index from 0) is the minimum characters to achieve the goal, after the first i operations.
+
+### Sample Input
+
+```log
+2
+a
+bab
+baa-
+```
+
+> 注意, 前三行都是输入, 第四行是操作序列.
+
+### Sample Output
+
+```log
+1
+1
+0
+0
+0
+```
+
+### HINT
+
+- "": he need input "a" to achieve the goal.
+- "b": he need input "a" to achieve the goal.
+
+- "ba": he need input nothing to achieve the goal.
+- "baa": he need input nothing to achieve the goal.
+- "ba": he need input nothing to achieve the goal.
+
+## 解答
+
+### 算法思想
+
+这个问题的核心是, 对于每个操作后形成的字符串, 我们需要找到最少再输入多少个字符, 就能使字符串的某个后缀是一个完整的字典单词. 如果直接对每次操作后的字符串进行暴力检查, 效率会非常低, 很容易超时.
+
+为了高效解决, 我们可以采用 预处理 + 快速查询 的思路, 使用 AC自动机 (Aho-Corasick Automaton) 结合 广度优先搜索 (BFS) 来实现.
+
+1. 构建AC自动机:
+ * 首先, 将所有字典单词构建成一棵 Trie树. 树上的每个节点代表一个前缀.
+ * 然后, 为Trie树构建 失败指针 (Failure Links). 对于任意节点 `u`, 它的失败指针指向的节点 `v` 所代表的字符串是 `u` 所代表字符串的最长真后缀, 且该后缀也必须是字典中某个单词的前缀. 这使得在匹��失败时可以快速跳转到下一个可能的状态.
+ * 在构建失败指针的同时, 传递"单词结尾"的标记. 如果一个节点的失败指针指向一个单词终点, 那么该节点也应被视为一个匹配成功的状态, 因为以它为后缀的字符串必然也包含了一个完整的字典单词.
+
+2. 预计算最短距离:
+ * 问题的关键是计算从AC自动机中的每个状态(节点)出发, 最少需要多少步(输入多少字符)才能到达一个代表完整单词的节点.
+ * 这可以转化为一个图上的最短路径问题. 我们使用 反向多源BFS 来解决:
+ * 源: 将所有代表完整单词的节点(即 `isEndOfWord` 为 `true` 的节点)作为BFS的初始队列, 它们的距离 `minLen` 设为 0.
+ * 反向遍历: 为了实现反向遍历, 我们在构建Trie时额外记录每个节点的父节点 (`parents`). BFS从所有单词终点开始, 沿着 `parents` 指针向上(向根节点方向)进行遍历.
+ * 每向上走一步, 距离就加1. 由于BFS的性质, 当我们第一次访问到一个节点时, 所记录的路径长度就是最短的.
+ * 通过这个过程, 我们预计算出了Trie中每一个节点到最近单词终点的最短距离, 并保存在 `minLen` 字段中.
+
+3. 处理操作序列:
+ * 在完成了预计算之后, 处理操作序列就变得非常简单.
+ * 我们维护一个指针指向AC自动机中的当前状态节点, 初始时在根节点.
+ * 遍历操作序列:
+ * 如果输入一个字符, 就从当前节点沿着对应的 `children` 指针走到下一个状态.
+ * 如果遇到退格符 `-`, 就回退到路径中的上一个节点.
+ * 在每次操作之后, 当前状态节点的 `minLen` 值就是该状态下需要补全的最小字符数, 即为当前问题的答案. 我们直接查询并记录即可.
+
+这种"预处理+查询"的模式将主要计算量集中在初始构建阶段, 使得后续每次查询都非常高效, 从而满足了题目的性能要求.
diff --git a/2018fall/lab_5/lab_5_1151/pom.xml b/2018fall/lab_5/lab_5_1151/pom.xml
new file mode 100644
index 0000000..dd3b566
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_5
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab5
+ lab_5_1151
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_5/lab_5_1151/resources/01.data.in b/2018fall/lab_5/lab_5_1151/resources/01.data.in
new file mode 100644
index 0000000..c97d181
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/01.data.in
@@ -0,0 +1,4 @@
+2
+a
+bab
+baa-
diff --git a/2018fall/lab_5/lab_5_1151/resources/01.data.out b/2018fall/lab_5/lab_5_1151/resources/01.data.out
new file mode 100644
index 0000000..2182420
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/01.data.out
@@ -0,0 +1,5 @@
+1
+1
+0
+0
+0
diff --git a/2018fall/lab_5/lab_5_1151/resources/02.data.in b/2018fall/lab_5/lab_5_1151/resources/02.data.in
new file mode 100644
index 0000000..0351dd8
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/02.data.in
@@ -0,0 +1,4 @@
+1
+a
+-
+
diff --git a/2018fall/lab_5/lab_5_1151/resources/02.data.out b/2018fall/lab_5/lab_5_1151/resources/02.data.out
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/02.data.out
@@ -0,0 +1,2 @@
+1
+1
diff --git a/2018fall/lab_5/lab_5_1151/resources/03.data.in b/2018fall/lab_5/lab_5_1151/resources/03.data.in
new file mode 100644
index 0000000..ac3a98e
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/03.data.in
@@ -0,0 +1,4 @@
+1
+abc
+abc
+
diff --git a/2018fall/lab_5/lab_5_1151/resources/03.data.out b/2018fall/lab_5/lab_5_1151/resources/03.data.out
new file mode 100644
index 0000000..4cc9dc3
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/03.data.out
@@ -0,0 +1,4 @@
+3
+2
+1
+0
diff --git a/2018fall/lab_5/lab_5_1151/resources/04.data.in b/2018fall/lab_5/lab_5_1151/resources/04.data.in
new file mode 100644
index 0000000..00d605a
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/04.data.in
@@ -0,0 +1,5 @@
+2
+a
+a
+----
+
diff --git a/2018fall/lab_5/lab_5_1151/resources/04.data.out b/2018fall/lab_5/lab_5_1151/resources/04.data.out
new file mode 100644
index 0000000..627e109
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/resources/04.data.out
@@ -0,0 +1,5 @@
+1
+1
+1
+1
+1
diff --git a/2018fall/lab_5/lab_5_1151/src/Main.java b/2018fall/lab_5/lab_5_1151/src/Main.java
new file mode 100644
index 0000000..c5addf2
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/src/Main.java
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.*;
+
+public final class Main {
+
+ private static final int ALPHABET_SIZE = 26;
+
+ static class TrieNode {
+ final TrieNode[] children = new TrieNode[ALPHABET_SIZE];
+ TrieNode parent; // OPTIMIZED: Use single parent reference instead of a List
+ TrieNode fail;
+ boolean isEndOfWord;
+ int minLen = -1; // Min length to a word
+ boolean visited; // OPTIMIZED: For BFS traversal instead of a Map
+ }
+
+ public static final class TestCase {
+ final List words;
+ final String opSeq;
+
+ public TestCase(List words, String opSeq) {
+ this.words = words;
+ this.opSeq = opSeq;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int n = in.nextInt();
+ final List words = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ words.add(in.next());
+ }
+ final String opSeq = in.next();
+ return List.of(new TestCase(words, opSeq));
+ }
+
+ public static List> cal(List inputs) {
+ final List> allResults = new ArrayList<>();
+ for (final TestCase tc : inputs) {
+ // 1. Build Trie and parent links
+ final TrieNode root = new TrieNode();
+ int totalLength = 0; // For capacity estimation
+ for (final String word : tc.words) {
+ TrieNode curr = root;
+ totalLength += word.length();
+ for (final char ch : word.toCharArray()) {
+ final int index = ch - 'a';
+ if (curr.children[index] == null) {
+ curr.children[index] = new TrieNode();
+ curr.children[index].parent = curr; // OPTIMIZED: Set single parent
+ }
+ curr = curr.children[index];
+ }
+ curr.isEndOfWord = true;
+ }
+
+ // 2. Build Fail links (BFS) and collect all nodes
+ final Queue queue = new ArrayDeque<>();
+ // OPTIMIZED: Pre-allocate capacity for allNodes
+ final List allNodes = new ArrayList<>(totalLength + 1);
+ final Queue minLenQueue = new ArrayDeque<>();
+
+ root.visited = true;
+ allNodes.add(root);
+
+ for (int i = 0; i < ALPHABET_SIZE; i++) {
+ if (root.children[i] != null) {
+ root.children[i].fail = root;
+ queue.add(root.children[i]);
+ // OPTIMIZED: Redundant visited check removed, root children are always new
+ root.children[i].visited = true;
+ allNodes.add(root.children[i]);
+ } else {
+ root.children[i] = root;
+ }
+ }
+
+ while (!queue.isEmpty()) {
+ final TrieNode curr = queue.poll();
+ curr.isEndOfWord |= curr.fail.isEndOfWord; // Propagate end of word
+
+ for (int i = 0; i < ALPHABET_SIZE; i++) {
+ if (curr.children[i] != null) {
+ curr.children[i].fail = curr.fail.children[i];
+ queue.add(curr.children[i]);
+ if (!curr.children[i].visited) {
+ curr.children[i].visited = true;
+ allNodes.add(curr.children[i]);
+ }
+ } else {
+ curr.children[i] = curr.fail.children[i];
+ }
+ }
+ }
+
+ // 3. BFS from word-end nodes to calculate minLen
+ for (final TrieNode node : allNodes) {
+ if (node.isEndOfWord) {
+ node.minLen = 0;
+ minLenQueue.add(node);
+ }
+ }
+
+ while (!minLenQueue.isEmpty()) {
+ final TrieNode u = minLenQueue.poll();
+ // Traverse backwards using parent links
+ final TrieNode v = u.parent;
+ if (v != null && v.minLen == -1) {
+ v.minLen = u.minLen + 1;
+ minLenQueue.add(v);
+ }
+ }
+
+
+ // 4. Process operations
+ final List results = new ArrayList<>(tc.opSeq.length() + 1);
+ // OPTIMIZED: Use ArrayDeque as a stack for path tracking
+ final Deque path = new ArrayDeque<>();
+ path.add(root);
+ results.add(root.minLen); // Initial state
+
+ TrieNode currentNode = root;
+ for (final char op : tc.opSeq.toCharArray()) {
+ if (op == '-') {
+ if (path.size() > 1) {
+ path.removeLast();
+ }
+ currentNode = path.peekLast();
+ } else {
+ final int index = op - 'a';
+ currentNode = currentNode.children[index];
+ path.addLast(currentNode);
+ }
+ results.add(currentNode.minLen);
+ }
+ allResults.add(results);
+ }
+ return allResults;
+ }
+
+ public static void output(List> allDecides) {
+ // OPTIMIZED: Use StringBuilder for faster I/O
+ final StringBuilder sb = new StringBuilder();
+ for (final List decides : allDecides) {
+ for (final int decide : decides) {
+ sb.append(decide).append('\n');
+ }
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_5/lab_5_1151/test/MainTest.java b/2018fall/lab_5/lab_5_1151/test/MainTest.java
new file mode 100644
index 0000000..d8968fc
--- /dev/null
+++ b/2018fall/lab_5/lab_5_1151/test/MainTest.java
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_1() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "01.data.in", "01.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "02.data.in", "02.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("02.data.out", "02.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+ @Test
+ public void test_3() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "03.data.in", "03.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("03.data.out", "03.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+ @Test
+ public void test_4() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "04.data.in", "04.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("04.data.out", "04.test.out");
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+}
diff --git a/2018fall/lab_5/pom.xml b/2018fall/lab_5/pom.xml
index d88cdc9..2512ac4 100644
--- a/2018fall/lab_5/pom.xml
+++ b/2018fall/lab_5/pom.xml
@@ -23,6 +23,7 @@
lab_5_1148
lab_5_1149
lab_5_1150
+ lab_5_1151
From 2c6fa1645490f68e7b7b0bd559bc1bc7736a9c1c Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 07:28:44 +0000
Subject: [PATCH 16/46] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=202018fall/lab?=
=?UTF-8?q?5=20=E6=95=B4=E4=BD=93=E8=AF=84=E4=BB=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/README.md | 64 ++++++++++++++++++++++++++++++++++++++--
AGENTS.md | 1 +
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/2018fall/lab_5/README.md b/2018fall/lab_5/README.md
index 37e5a25..427f569 100644
--- a/2018fall/lab_5/README.md
+++ b/2018fall/lab_5/README.md
@@ -23,7 +23,67 @@ Read the samples carefully can help you understand the problem.
+ [x] problem C: lab_5_1047
+ [x] problem D: lab_5_1148
+ [x] problem E: lab_5_1149
-+ [ ] problem F: lab_5_1150
-+ [ ] problem G: lab_5_1151
++ [x] problem F: lab_5_1150
++ [x] problem G: lab_5_1151
## 总体评价
+
+本次实验的设计虽然在选题上有部分合理性, 但其核心问题依然突出: 它并未能成为一个优秀的 KMP 算法练习平台, 反而迅速演变成了一场对更高级, 更偏门算法的"突击测验", 严重偏离了教学与巩固的初衷.
+
+实验的设计思路似乎是: 以 KMP 为起点, 考察学生举一反三的能力. 然而, 这种"举一反三"的跨度过大, 从 KMP 直接跳跃到了 Z-algorithm, AC 自动机, 以及 "对答案二分" 等完全不同且难度陡增的领域.
+
+`submit.csv` 的数据依旧冰冷地证明, 学生面临的障碍并非 KMP 算法本身, 而是那些远超课程教学范围的, 需要专门知识和大量训练才能掌握的算法技巧.
+
+### 各题目的具体
+
+#### A 题 (Rhymes) 与 B 题 (Wildcard):
+
+作为热身题, 这两题的设置是合理的, 旨在让学生进入状态. B 题惨淡的提交数据 (57 AC vs 499 WA) 也确实起到了检验学生代码严谨性的作用.
+
+#### C 题 (KMP Search):
+
+在学习了 KMP 之后, 这道题是一道完美的, 直接的应用题. 它很好地考察了学生对 KMP 算法模板的掌握程度. 这是一个完全合理的题目.
+
+#### E 题 (Longest Prefix as Suffix):
+
+这道题是教学设计出现偏差的第一个信号. 它的解法虽然基于 KMP 的预处理数组, 但依赖于一个非常规的, "抖机灵"式的技巧 (拼接字符串).
+
+这并非在考察学生对 KMP 算法工作原理的深入理解, 而是在考察他们是否接触过这类特定的竞赛题型.
+
+对于初学者而言, 这是一个糟糕的教学案例, 它鼓励的是对技巧的记忆而非对原理的探索.
+
+#### D 题 (Punchline):
+
+这是最能体现设计失败的题目. 在一个 KMP 的练习实验中, 突然要求学生掌握 Z-algorithm, 甚至需要 RMQ (区间最值查询) 的知识来进行优化.
+
+这相当于在教完加法后, 考试中突然出现一道微积分题目. Z-algorithm 与 KMP 虽然同属字符串处理领域, 但其思想和实现细节完全不同.
+
+D 题高达 618 次 WA 和 810 次 RE 的提交结果, 明确显示了学生面对这种"知识断崖"时的无所适从.
+
+#### F 题 (Longest Common Substring):
+
+此题引入了"对答案二分"这一元算法 (meta-algorithm).
+
+这本身就是一个重要的, 需要专门讲解和练习的算法思想, 将它与字符串问题结合, 作为 KMP 课后练习的一部分, 显然是不合适的.
+
+它将学生的挑战从"如何应用 KMP"转移到了"如何构建二分模型"和"如何设计高效的验证函数"上, 完全偏离了主题.
+
+#### G 题 (AC Automaton):
+
+这道题是超纲的极致. AC 自动机是基于 Trie 的多模式匹配算法, 其复杂度和抽象程度远高于 KMP.
+
+将其放入一个大二学生的实验中, 尤其是在一个以 KMP 为主题的实验里, 是完全不现实的教学要求. 这道题存在的唯一意义, 似乎就是筛选出那些有长期算法竞赛训练背景的学生.
+
+### 结论:
+
+作为一个 KMP 算法的课后实验, Lab 5 是不合格的.
+
+它没能围绕 KMP 这一核心设计出一系列难度递进, 应用角度多样的练习题 (例如, 利用 KMP 的 `next` 数组求解字符串的循环节, 判断回文等).
+
+相反, 它仅仅以 KMP 为跳板, 轻率地将学生推向了更高级算法的深渊.
+
+这样的设计对于真心想要学好数据结构的学生来说是极具挫败感的.
+
+它没有奖励学生对 KMP 算法的深入理解和灵活应用, 反而惩罚了他们知识面的局限性.
+
+一个优秀的教学实验, 应当是围绕一个核心知识点搭建的"脚手架", 引导学生逐步攀升; 而不是非在此处设置一个"陷阱", 嘲笑那些没能直接飞跃过去的人.
diff --git a/AGENTS.md b/AGENTS.md
index d5d7d7d..616895d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,5 +1,6 @@
## 注意事项
+0. 使用 zh-CN 思考, 分析, 回答.
1. 使用 JDK11 语法, 尽量使用现代数据结构, 尽量使用final var不可变变量
2. 尽量遵守读-处理-输出分离的原则
3. 不使用任何中文标点
From 66ef182ce3958420194c896f3065b6c0c0d2fa9e Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 10:05:44 +0000
Subject: [PATCH 17/46] feat: add 2018fall/lab6-1152
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_5/lab_5_1148/README.md | 2 +-
2018fall/lab_6/README.md | 29 +++++
2018fall/lab_6/lab_6_1152/README.md | 67 ++++++++++
2018fall/lab_6/lab_6_1152/pom.xml | 22 ++++
.../lab_6/lab_6_1152/resources/01.data.in | 5 +
.../lab_6/lab_6_1152/resources/01.data.out | 1 +
2018fall/lab_6/lab_6_1152/src/Main.java | 115 ++++++++++++++++++
2018fall/lab_6/lab_6_1152/test/MainTest.java | 46 +++++++
2018fall/lab_6/pom.xml | 31 +++++
2018fall/lab_6/submit.csv | 9 ++
2018fall/pom.xml | 1 +
11 files changed, 327 insertions(+), 1 deletion(-)
create mode 100644 2018fall/lab_6/README.md
create mode 100644 2018fall/lab_6/lab_6_1152/README.md
create mode 100644 2018fall/lab_6/lab_6_1152/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1152/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1152/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1152/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1152/test/MainTest.java
create mode 100644 2018fall/lab_6/pom.xml
create mode 100644 2018fall/lab_6/submit.csv
diff --git a/2018fall/lab_5/lab_5_1148/README.md b/2018fall/lab_5/lab_5_1148/README.md
index 14a5f3d..7f0c5be 100644
--- a/2018fall/lab_5/lab_5_1148/README.md
+++ b/2018fall/lab_5/lab_5_1148/README.md
@@ -62,7 +62,7 @@ abababa
- 前缀:[0, len-1]
- 中间:[j, j+len-1]
- 后缀:[n-len, n-1]
-- 为不重叠,需满足 j ≥ len 且 j+len-1 ≤ n-len-1,即中间“起点” j ∈ [len, n-2*len]。
+- 为不重叠,需满足 j ≥ len 且 j+len-1 <= n-len-1,即中间“起点” j ∈ [len, n-2*len]。
- 用 Z 判断:存在 j ∈ [len, n-2*len] 使得 z[j] ≥ len,则中间合法出现一次。
算法流程:
diff --git a/2018fall/lab_6/README.md b/2018fall/lab_6/README.md
new file mode 100644
index 0000000..59cc59c
--- /dev/null
+++ b/2018fall/lab_6/README.md
@@ -0,0 +1,29 @@
+# 2018fall-lab5
+
+Welcome to (autumn) DSAA lab 6! Enjoy this Lab!
+
+There are seven problems for you to solve. Two of them are bonus. Read the problem description carefully.
+
+Compulsory problems:
+
++ A(easy): 10
++ B(easy): 10
++ C(easy): 20
++ D(median): 25
++ E(median): 25
++ Bonus problem: F(hard): 30
++ Bonus problem: G(hard): 30
+
+Read the samples carefully can help you understand the problem.
+
+## Stack And Queue
+
++ [x] problem A: lab_6_1152
++ [ ] problem B: lab_6_1153
++ [ ] problem C: lab_6_1154
++ [ ] problem D: lab_6_1155
++ [ ] problem E: lab_6_1156
++ [ ] problem F: lab_6_1157
++ [ ] problem G: lab_6_1158
+
+## 总体评价
diff --git a/2018fall/lab_6/lab_6_1152/README.md b/2018fall/lab_6/lab_6_1152/README.md
new file mode 100644
index 0000000..ab8fe4f
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1152/README.md
@@ -0,0 +1,67 @@
+## Description
+
+Write a program to print all the leaves of the given tree, numbered from 1 to N. The root of the tree is node 1.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 10), which is the number of test cases.
+
+For each test data:
+
+The first line contains an integer N (2 <= N <= 10^4) — the number of the nodes.
+
+Each of the next N - 1 lines contain two integers a and b, which means there is an edge between node a and b (1 <= a, b <= N).
+
+### Output
+
+For each case please, print all the leaves of the given tree, in ascending order.
+
+For the tree has multiple leaf nodes, there is a blank between two leaf nodes, and ‘\n’ at the end of each line.
+
+### Sample Input
+
+```log
+1
+4
+1 2
+2 3
+3 4
+```
+
+### Sample Output
+
+``` log
+4
+```
+
+
+## 解答
+
+本题要求我们找出给定树中所有的叶子节点. 题目明确指出节点 1 是树的根.
+
+### 算法思想
+
+根据图论的定义, 在一棵树中, 度为 1 的节点被称为叶子节点.
+
+题目还附加了一个条件: 节点 1 是根节点. 在树的语境下, "叶子节点" 通常指那些没有子节点的非根节点.
+
+因此, 我们可以将本题的"叶子节点"定义为: 一个度为 1, 且编号不为 1 的节点.
+
+基于这个定义, 我们可以设计一个简单而高效的算法:
+
+1. 数据结构: 我们首先需要一种方式来表示树的结构. 邻接表 (`List>`) 是一个非常适合的选择. 我们可以创建一个大小为 `N+1` 的邻接表 `adj`, 其中 `adj.get(i)` 存储了所有与节点 `i` 直接相连的节点.
+
+2. 构建树: 读取输入的 `N-1` 条边. 对于每一条边 `(u, v)`, 我们同时在 `u` 的邻接列表里添加 `v`, 并在 `v` 的邻接列表里添加 `u`. 完成后, `adj.get(i).size()` 就代表了节点 `i` 的度.
+
+3. 寻找叶子节点:
++ 我们遍历所有可能的非根节点, 即从节点 2 到节点 N.
++ 对于每一个节点 `i`, 我们检查它在邻接表中的度, 即 `adj.get(i).size()`.
++ 如果度为 1, 那么节点 `i` 就符合我们对叶子节点的定义, 将它添加到一个结果列表中.
+
+4. 处理特殊情况:
++ 当 `N=2` 时, 树的结构只有一条边 `1-2`. 此时, 节点 1 是根, 它的度是 1. 节点 2 的度也是 1. 根据我们的定义 (非根节点且度为 1), 节点 2 是唯一的叶子节点.
++ 我的代码中对 `N=2` 的情况进行了单独处理, 但实际上, 通用逻辑 (遍历 2 到 N) 已经可以完美覆盖这种情况.
+
+5. 输出: 由于我们是从 2 到 N 按升序遍历的, 找到的叶子节点自然也是有序的. 最后, 将结果列表按题目要求的格式输出即可.
+
+这个算法的时间复杂度主要由建图和遍历节点两部分构成, 均为线性时间, 因此总复杂度为 O(N), 足以高效地解决本题.
diff --git a/2018fall/lab_6/lab_6_1152/pom.xml b/2018fall/lab_6/lab_6_1152/pom.xml
new file mode 100644
index 0000000..de66a05
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1152/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1152
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1152/resources/01.data.in b/2018fall/lab_6/lab_6_1152/resources/01.data.in
new file mode 100644
index 0000000..a75bf1a
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1152/resources/01.data.in
@@ -0,0 +1,5 @@
+1
+4
+1 2
+2 3
+3 4
diff --git a/2018fall/lab_6/lab_6_1152/resources/01.data.out b/2018fall/lab_6/lab_6_1152/resources/01.data.out
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1152/resources/01.data.out
@@ -0,0 +1 @@
+4
diff --git a/2018fall/lab_6/lab_6_1152/src/Main.java b/2018fall/lab_6/lab_6_1152/src/Main.java
new file mode 100644
index 0000000..275f663
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1152/src/Main.java
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.stream.Collectors;
+
+public final class Main {
+
+ public static final class TestCase {
+ final int n;
+ final List> adj;
+
+ public TestCase(int n, List> adj) {
+ this.n = n;
+ this.adj = adj;
+ }
+ }
+
+ public static List reader() {
+ final var in = new Reader();
+ final int testCases = in.nextInt();
+ assert (testCases >= 1) && (testCases <= 10) : "T must be between 1 and 10";
+ final List cases = new ArrayList<>(testCases);
+ for (int i = 0; i < testCases; i++) {
+ final int n = in.nextInt();
+ assert (n >= 2) && (n <= 10000) : "N must be between 2 and 10^4";
+ final List> adj = new ArrayList<>(n + 1);
+ for (int j = 0; j <= n; j++) {
+ adj.add(new ArrayList<>());
+ }
+ for (int j = 0; j < n - 1; j++) {
+ final int u = in.nextInt();
+ final int v = in.nextInt();
+ assert (u >= 1) && (u <= n) : "u must be between 1 and N";
+ assert (v >= 1) && (v <= n) : "v must be between 1 and N";
+ adj.get(u).add(v);
+ adj.get(v).add(u);
+ }
+ cases.add(new TestCase(n, adj));
+ }
+ return cases;
+ }
+
+ public static List> cal(List inputs) {
+ final List> results = new ArrayList<>();
+ for (final var tc : inputs) {
+ final List leaves = new ArrayList<>();
+ if (tc.n == 2) {
+ // For N=2, the tree is just 1-2. Node 1 is the root, so node 2 is the only leaf.
+ leaves.add(2);
+ } else {
+ // A leaf is a non-root node with degree 1.
+ // We iterate from 2 to N to exclude the root.
+ for (int i = 2; i <= tc.n; i++) {
+ if (tc.adj.get(i).size() == 1) {
+ leaves.add(i);
+ }
+ }
+ }
+ results.add(leaves);
+ }
+ return results;
+ }
+
+ public static void output(List> allDecides) {
+ final StringBuilder sb = new StringBuilder();
+ for (final var decides : allDecides) {
+ if (!decides.isEmpty()) {
+ sb.append(
+ decides.stream()
+ .map(String::valueOf)
+ .collect(Collectors.joining(" "))
+ );
+ }
+ sb.append('\n');
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(String[] args) {
+ output(cal(reader()));
+ }
+
+ // refactor from https://github.com/Kattis/kattio/blob/master/Kattio.java
+ // url: https://raw.githubusercontent.com/Kattis/kattio/master/Kattio.java
+ // license: MIT
+ public static final class Reader {
+ public final BufferedReader br;
+ public StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+}
diff --git a/2018fall/lab_6/lab_6_1152/test/MainTest.java b/2018fall/lab_6/lab_6_1152/test/MainTest.java
new file mode 100644
index 0000000..625f5ef
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1152/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/pom.xml b/2018fall/lab_6/pom.xml
new file mode 100644
index 0000000..e5ae9ef
--- /dev/null
+++ b/2018fall/lab_6/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template
+ 2018fall
+ ${revision}
+ ./../pom.xml
+
+
+ pom
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+ lab_6_1152
+
+
+
+ nanoseeds.algorithm-template
+ test_include_package
+ ${revision}
+ test
+
+
+
+
diff --git a/2018fall/lab_6/submit.csv b/2018fall/lab_6/submit.csv
new file mode 100644
index 0000000..ee9bf58
--- /dev/null
+++ b/2018fall/lab_6/submit.csv
@@ -0,0 +1,9 @@
+AC, PE, WA, TLE, MLE, OLE, RE, CE, TR, Total , C, C++, Java
+A, 195, 24, 285, 15, 4, 6, 33, 12, 40, 614, 27, 76, 511
+B, 222, 12, 6, 27, 35, 302, 15, 62, 225
+C, 199, 46, 425, 210, 2, 4, 184, 23, 156, 1249, 61, 228, 960
+D, 153, 191, 26, 16, 85, 77, 166, 714, 46, 244, 424
+E, 65, 226, 138, 1, 4, 38, 10, 92, 574, 24, 74, 476
+F, 148, 240, 55, 6, 8, 73, 21, 197, 748, 18, 138, 592
+G, 29, 32, 35, 3, 5, 3, 7, 114, 9, 33, 72
+Total, 1011, 70, 1411, 479, 32, 22, 424, 173, 693, 4315, 200, 855, 3260
diff --git a/2018fall/pom.xml b/2018fall/pom.xml
index 2b2411a..f8f5d4f 100644
--- a/2018fall/pom.xml
+++ b/2018fall/pom.xml
@@ -21,5 +21,6 @@
lab_3
lab_4
lab_5
+ lab_6
From 86e4e1846f9be750e452e24dbd811cf195f9e6e0 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 11:42:38 +0000
Subject: [PATCH 18/46] feat: add 2018fall/lab6-1158
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
gemini2.5pro, gpt5都无法给出正确结果, 抬一级到 Claude Opus4.1才给出暴力解
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_6/README.md | 2 +-
2018fall/lab_6/lab_6_1158/README.md | 102 +++++++++++++++++
2018fall/lab_6/lab_6_1158/pom.xml | 22 ++++
.../lab_6/lab_6_1158/resources/01.data.in | 9 ++
.../lab_6/lab_6_1158/resources/01.data.out | 1 +
.../lab_6/lab_6_1158/resources/02.data.in | 3 +
.../lab_6/lab_6_1158/resources/02.data.out | 1 +
.../lab_6/lab_6_1158/resources/03.data.in | 7 ++
.../lab_6/lab_6_1158/resources/03.data.out | 1 +
.../lab_6/lab_6_1158/resources/04.data.in | 7 ++
.../lab_6/lab_6_1158/resources/04.data.out | 1 +
2018fall/lab_6/lab_6_1158/src/Main.java | 106 ++++++++++++++++++
2018fall/lab_6/lab_6_1158/test/MainTest.java | 80 +++++++++++++
2018fall/lab_6/pom.xml | 1 +
14 files changed, 342 insertions(+), 1 deletion(-)
create mode 100644 2018fall/lab_6/lab_6_1158/README.md
create mode 100644 2018fall/lab_6/lab_6_1158/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1158/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1158/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1158/resources/02.data.in
create mode 100644 2018fall/lab_6/lab_6_1158/resources/02.data.out
create mode 100644 2018fall/lab_6/lab_6_1158/resources/03.data.in
create mode 100644 2018fall/lab_6/lab_6_1158/resources/03.data.out
create mode 100644 2018fall/lab_6/lab_6_1158/resources/04.data.in
create mode 100644 2018fall/lab_6/lab_6_1158/resources/04.data.out
create mode 100644 2018fall/lab_6/lab_6_1158/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1158/test/MainTest.java
diff --git a/2018fall/lab_6/README.md b/2018fall/lab_6/README.md
index 59cc59c..6a2fc71 100644
--- a/2018fall/lab_6/README.md
+++ b/2018fall/lab_6/README.md
@@ -24,6 +24,6 @@ Read the samples carefully can help you understand the problem.
+ [ ] problem D: lab_6_1155
+ [ ] problem E: lab_6_1156
+ [ ] problem F: lab_6_1157
-+ [ ] problem G: lab_6_1158
++ [x] problem G: lab_6_1158
## 总体评价
diff --git a/2018fall/lab_6/lab_6_1158/README.md b/2018fall/lab_6/lab_6_1158/README.md
new file mode 100644
index 0000000..d4ca6c2
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/README.md
@@ -0,0 +1,102 @@
+## Description
+
+Hong has a tree, whose vertices are conveniently labeled by 1, 2, ..., n. Each node has a weight $w_{i}$
+
+A set with m nodes $v_{1}, v_{2}, ..., v_{m}$ is a Hong Set if:
+
+The tree induced by this set is connected.
+
+After we sort these nodes in set by their weights in ascending order, we get $u_{1}, u_{2}, ..., u_{m}$, (that is, $w\_u_{i} < w\_u_{i+1}$ for i from 1 to m-1).
+
+For any node x in the path from $u_{i}$ to $u_{i+1} $(excluding $u+{i}$ and $u_{i+1}$), should satisfy $w\_x < w\_u_{i}$.
+
+Your task is to find the maximum size of Hong Set in a given tree.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 10), which is the number of test cases.
+
+For each test data:
+
+The first line contains two integers N (1 <= N <= 200000) — the number of the nodes.
+
+The second line contains N integers $w_{1}…w_{n}$ (1 <= $w_{i}$ <= 10^9).
+
+Each of the next N-1 lines contain two integers a and b, which means there is an edge between node a and b.
+
+> 注意, 这里有且只有 N-1 条边, 边对于节点来说非常稀疏
+
+### Output
+
+For each case please print the maximum size of Hong Set.
+
+### Sample Input
+
+```log
+1
+7
+3 30 350 100 200 300 400
+1 2
+2 3
+3 4
+4 5
+5 6
+6 7
+```
+
+### Sample Output
+
+```log
+5
+```
+
+#### 思考input-output
+
+首先, 我们需要清晰地理解 "Hong Set" 的两个定义条件:
+
+连通性: 集合中的所有节点在树上必须是连通的.
+权重路径约束: 将集合中的节点按权重从小到大排序, 得到 u_1, u_2, ..., u_m. 对于任意相邻的两个节点 u_i 和 u_{i+1}, 它们在原树中的路径上, 所有 不属于 该集合的中间节点 x 的权重, 都必须小于 u_i 的权重 (w_x < w_{u_i}).
+
+现在, 我们来看一下这个具体的用例:
+
+树的结构: 1-2-3-4-5-6-7, 这是一条直线.
+
+节点权重:
++ w_1 = 3
++ w_2 = 30
++ w_3 = 350
++ w_4 = 100
++ w_5 = 200
++ w_6 = 300
++ w_7 = 400
+
+为什么答案是 5?
+
+之所以输出是 5, 是因为存在一个合法的 `Hong Set {3, 4, 5, 6, 7}.`
+
+这个集合的节点在原树中是连通的 (它们构成了路径 3-4-5-6-7).
+
+将它们按权重排序后为: 4(100), 5(200), 6(300), 3(350), 7(400).
+
+在检查任意两个权重相邻的节点 (如 6 和 3) 之间的路径时, 路径上的所有中间节点 (如 4, 5) 本身也属于这个集合.
+
+因此, 路径上不存在 不属于 该集合的 "过路" 节点, 所以权重约束条件天然满足.
+
+## 解法
+
+核心思想: 算法尝试将树中的每一个节点 i (从 1 到 N) 作为其所在 "Hong Set" 中权重最小的节点(即排序后的 u_1)。
+
+执行流程:
+
+外层循环遍历所有节点 i,将其视为潜在的起始节点。
+
+对于每个 i,调用一个 dfs(i, -1, w[i]) 函数。
+
+dfs(u, parent, minWeight) 函数的目的是,从节点 u 开始,向上(向权重更大的节点)扩展,构建一个以 i 为权重最小节点的有效 Hong Set,并计算其大小。
+
+在 dfs 内部,它会递归地访问所有权重比当前节点 u 更大的邻居节点 v,并将它们的子集大小累加到结果中。
+
+复杂度: 对于每个节点,都可能进行一次深度优先搜索,其时间复杂度大致为 O(N)。由于外层循环也为 O(N),总时间复杂度近似为 O(N²)。对于 N 高达 200,000 的情况,这个解法会可能因为超时而无法通过所有测试用例,但它逻辑直接,易于理解。
+
+> 这个解法可以作为对拍数据的生成器, 已通过测试
+
diff --git a/2018fall/lab_6/lab_6_1158/pom.xml b/2018fall/lab_6/lab_6_1158/pom.xml
new file mode 100644
index 0000000..bb86787
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1158
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1158/resources/01.data.in b/2018fall/lab_6/lab_6_1158/resources/01.data.in
new file mode 100644
index 0000000..2d1e688
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/01.data.in
@@ -0,0 +1,9 @@
+1
+7
+3 30 350 100 200 300 400
+1 2
+2 3
+3 4
+4 5
+5 6
+6 7
diff --git a/2018fall/lab_6/lab_6_1158/resources/01.data.out b/2018fall/lab_6/lab_6_1158/resources/01.data.out
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/01.data.out
@@ -0,0 +1 @@
+5
diff --git a/2018fall/lab_6/lab_6_1158/resources/02.data.in b/2018fall/lab_6/lab_6_1158/resources/02.data.in
new file mode 100644
index 0000000..2049ba2
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/02.data.in
@@ -0,0 +1,3 @@
+1
+1
+10
diff --git a/2018fall/lab_6/lab_6_1158/resources/02.data.out b/2018fall/lab_6/lab_6_1158/resources/02.data.out
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/02.data.out
@@ -0,0 +1 @@
+1
diff --git a/2018fall/lab_6/lab_6_1158/resources/03.data.in b/2018fall/lab_6/lab_6_1158/resources/03.data.in
new file mode 100644
index 0000000..dbb17cf
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/03.data.in
@@ -0,0 +1,7 @@
+1
+5
+1 2 3 4 5
+1 2
+2 3
+3 4
+4 5
diff --git a/2018fall/lab_6/lab_6_1158/resources/03.data.out b/2018fall/lab_6/lab_6_1158/resources/03.data.out
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/03.data.out
@@ -0,0 +1 @@
+5
diff --git a/2018fall/lab_6/lab_6_1158/resources/04.data.in b/2018fall/lab_6/lab_6_1158/resources/04.data.in
new file mode 100644
index 0000000..82fac64
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/04.data.in
@@ -0,0 +1,7 @@
+1
+5
+10 1 1 1 1
+1 2
+1 3
+1 4
+1 5
diff --git a/2018fall/lab_6/lab_6_1158/resources/04.data.out b/2018fall/lab_6/lab_6_1158/resources/04.data.out
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/resources/04.data.out
@@ -0,0 +1 @@
+2
diff --git a/2018fall/lab_6/lab_6_1158/src/Main.java b/2018fall/lab_6/lab_6_1158/src/Main.java
new file mode 100644
index 0000000..c781212
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/src/Main.java
@@ -0,0 +1,106 @@
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.*;
+
+public class Main {
+ static int n;
+ static long[] w;
+ static List[] adj;
+
+ // 计算以u为根,parent为父节点时的最大Hong Set大小
+ static int dfs(int u, int parent, long minWeight) {
+ // 如果当前节点权重小于最小权重要求,不能作为Hong Set的一部分
+ if (w[u] < minWeight) {
+ // 但可以作为路径上的中间节点,继续向下搜索
+ int maxFromChildren = 0;
+ for (int v : adj[u]) {
+ if (v != parent) {
+ maxFromChildren = Math.max(maxFromChildren, dfs(v, u, minWeight));
+ }
+ }
+ return maxFromChildren;
+ }
+
+ // u可以作为Hong Set的一部分
+ int result = 1; // 包含u本身
+
+ // 收集所有可以加入的子节点
+ final List validChildren = new ArrayList<>();
+ for (int v : adj[u]) {
+ if (v != parent && w[v] > w[u]) {
+ validChildren.add(v);
+ }
+ }
+
+ // 对每个有效子节点,计算其贡献
+ for (int v : validChildren) {
+ result += dfs(v, u, w[u]);
+ }
+
+ return result;
+ }
+
+ public static void main(String[] args) {
+ final var sc = new Reader();
+ int T = sc.nextInt();
+
+ while (T-- > 0) {
+ n = sc.nextInt();
+ w = new long[n + 1];
+ adj = new List[n + 1];
+
+ for (int i = 1; i <= n; i++) {
+ w[i] = sc.nextLong();
+ adj[i] = new ArrayList<>();
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ int a = sc.nextInt();
+ int b = sc.nextInt();
+ adj[a].add(b);
+ adj[b].add(a);
+ }
+
+ int maxSize = 0;
+
+ // 枚举每个节点作为Hong Set的起始节点
+ for (int i = 1; i <= n; i++) {
+ final int size = dfs(i, -1, w[i]);
+ maxSize = Math.max(maxSize, size);
+ }
+
+ System.out.print(maxSize);
+ System.out.print('\n');
+ }
+ }
+
+ public static final class Reader {
+ public final BufferedReader br;
+ public StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ st = new StringTokenizer(br.readLine());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ int nextInt() {
+ return Integer.parseInt(next());
+ }
+
+ long nextLong() {
+ return Long.parseLong(next());
+ }
+
+ }
+}
diff --git a/2018fall/lab_6/lab_6_1158/test/MainTest.java b/2018fall/lab_6/lab_6_1158/test/MainTest.java
new file mode 100644
index 0000000..b2f8fc8
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1158/test/MainTest.java
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_1() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "01.data.in", "01.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "02.data.in", "02.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("02.data.out", "02.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+
+ @Test
+ public void test_3() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "03.data.in", "03.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("03.data.out", "03.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+
+ @Test
+ public void test_4() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH, "04.data.in", "04.test.out")) {
+ Main.main(new String[]{});
+ final Pair p = redirect.compare_double("04.data.out", "04.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/pom.xml b/2018fall/lab_6/pom.xml
index e5ae9ef..f9c2825 100644
--- a/2018fall/lab_6/pom.xml
+++ b/2018fall/lab_6/pom.xml
@@ -18,6 +18,7 @@
${project.groupId}.${project.artifactId}
lab_6_1152
+ lab_6_1158
From 3cff56fb9e83ae1bb7329a0c2cc630da7582e3a3 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:12:40 +0000
Subject: [PATCH 19/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?6-1153?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_6/lab_6_1153/README.md | 62 +++++++
2018fall/lab_6/lab_6_1153/pom.xml | 22 +++
.../lab_6/lab_6_1153/resources/01.data.in | 10 +
.../lab_6/lab_6_1153/resources/01.data.out | 3 +
2018fall/lab_6/lab_6_1153/src/Main.java | 174 ++++++++++++++++++
2018fall/lab_6/lab_6_1153/test/MainTest.java | 46 +++++
2018fall/lab_6/pom.xml | 1 +
7 files changed, 318 insertions(+)
create mode 100644 2018fall/lab_6/lab_6_1153/README.md
create mode 100644 2018fall/lab_6/lab_6_1153/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1153/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1153/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1153/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1153/test/MainTest.java
diff --git a/2018fall/lab_6/lab_6_1153/README.md b/2018fall/lab_6/lab_6_1153/README.md
new file mode 100644
index 0000000..4d4ac9a
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1153/README.md
@@ -0,0 +1,62 @@
+## Description
+
+Write a program to print the pre order, in order and post order traversal of the given binary tree.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 10), which is the number of test cases.
+
+For each test data:
+
+The first line contains one integer N (1 <= N <= 10^4) - the number of nodes.
+
+Each of the next N - 1 lines contain two integers a and b, which means node a is the father of node b (1 <= a, b <= N). If a node has two sons, the son appeared earlier is the left son and another is the right son. If a node only has one son, the son is the left son.
+
+### Output
+
+For each test case, print three lines: the pre order, in order and post order traversal of the given binary tree.
+
+### Sample Input
+
+```log
+1
+8
+1 4
+1 3
+4 2
+2 7
+3 5
+3 6
+6 8
+```
+
+### Sample Output
+
+```log
+1 4 2 7 3 5 6 8
+7 2 4 1 5 3 8 6
+7 2 4 5 8 6 3 1
+```
+
+## 解答
+
+### 算法思路
+
+- 输入处理
+ - 使用快读 Reader 读取整数流, 先读 T, 每个用例读 n 和接下來的 n-1 条边.
+ - 用两个数组 left[] 和 right[] 保存每个节点的左子节点和右子节点, 用 boolean 数组 isChild[] 标记被当作子节点出现的点. 根节点是未被标记的点.
+
+- 遍历实现
+ - 先序 preorder: 使用栈, 初始时如果 root != 0 则 push(root), 每次 pop u, 记录 u, 然后先 push 右子节点, 再 push 左子节点, 这样保证栈顶先访问左子树.
+ - 中序 inorder: 迭代实现, 从 root 开始沿左子链入栈, 到达最左后 pop 并访问, 然后转向該节点的右子树, 继续重复.
+ - 后序 postorder: 使用变形先序 root-right-left 将访问顺序记录到临时列表, 最後将该列表反转得到标准后序 left-right-root.
+
+- 复杂度与健壮性
+ - 时间复杂度: O(n) per test case, 空间复杂度: O(n).
+ - 在 reader 中加入 assert 检查输入约束, 例如 assert ((1 <= n) && (n <= 10000)); 以便在不合法输入时尽早发现错误.
+
+- 设计原则
+ - 遵循读-处理-输出分离: reader() 负责解析输入并构建 TestCase, cal() 负责计算三种遍历并返回输出行, output() 负责最终打印并保证每行以 '\n' 结尾.
+ - 避免深度递归, 所有遍历均采用迭代实现以提高稳定性.
+
+以上为算法核心思路, 实现细节见 src/Main.java 中的 reader, cal, output 的具体代码。
diff --git a/2018fall/lab_6/lab_6_1153/pom.xml b/2018fall/lab_6/lab_6_1153/pom.xml
new file mode 100644
index 0000000..6553784
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1153/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1153
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1153/resources/01.data.in b/2018fall/lab_6/lab_6_1153/resources/01.data.in
new file mode 100644
index 0000000..41ae6e0
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1153/resources/01.data.in
@@ -0,0 +1,10 @@
+1
+8
+1 4
+1 3
+4 2
+2 7
+3 5
+3 6
+6 8
+
diff --git a/2018fall/lab_6/lab_6_1153/resources/01.data.out b/2018fall/lab_6/lab_6_1153/resources/01.data.out
new file mode 100644
index 0000000..d7741cc
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1153/resources/01.data.out
@@ -0,0 +1,3 @@
+1 4 2 7 3 5 6 8
+7 2 4 1 5 3 8 6
+7 2 4 5 8 6 3 1
diff --git a/2018fall/lab_6/lab_6_1153/src/Main.java b/2018fall/lab_6/lab_6_1153/src/Main.java
new file mode 100644
index 0000000..83c6d51
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1153/src/Main.java
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.StringJoiner;
+
+public final class Main {
+
+ public static final class TestCase {
+ public final int n;
+ public final int[] left;
+ public final int[] right;
+ public final int root;
+
+ public TestCase(int n, int[] left, int[] right, int root) {
+ this.n = n;
+ this.left = left;
+ this.right = right;
+ this.root = root;
+ }
+ }
+
+ // reader: parse input into TestCase objects
+ public static List reader() {
+ final var in = new Reader();
+ final int T = in.nextInt();
+ assert ((1 <= T) && (T <= 10));
+ final List tests = new ArrayList<>(T);
+ for (int tc = 0; tc < T; tc++) {
+ final int n = in.nextInt();
+ assert ((1 <= n) && (n <= 10000));
+ final int[] left = new int[n + 1];
+ final int[] right = new int[n + 1];
+ final boolean[] isChild = new boolean[n + 1];
+ for (int i = 0; i < n - 1; i++) {
+ final int u = in.nextInt();
+ final int v = in.nextInt();
+ assert ((1 <= u) && (u <= n));
+ assert ((1 <= v) && (v <= n));
+ if (left[u] == 0) {
+ left[u] = v;
+ } else {
+ // place in right if left already occupied
+ assert (right[u] == 0) : "a node has more than two children";
+ right[u] = v;
+ }
+ isChild[v] = true;
+ }
+ int root = 1;
+ for (int i = 1; i <= n; i++) {
+ if (!isChild[i]) {
+ root = i;
+ break;
+ }
+ }
+ tests.add(new TestCase(n, left, right, root));
+ }
+ return tests;
+ }
+
+ // cal: perform traversals for each test case and return list of output lines
+ public static List cal(final List inputs) {
+ final List outLines = new ArrayList<>();
+ for (final var tc : inputs) {
+ final int root = tc.root;
+ // preorder
+ final List preorder = new ArrayList<>();
+ final Deque stack = new ArrayDeque<>();
+ if (root != 0) stack.push(root);
+ while (!stack.isEmpty()) {
+ final int u = stack.pop();
+ preorder.add(u);
+ final int r = tc.right[u];
+ final int l = tc.left[u];
+ if (r != 0) stack.push(r);
+ if (l != 0) stack.push(l);
+ }
+
+ // inorder (iterative)
+ final List inorder = new ArrayList<>();
+ int cur = root;
+ final Deque st2 = new ArrayDeque<>();
+ while ((cur != 0) || !st2.isEmpty()) {
+ while (cur != 0) {
+ st2.push(cur);
+ cur = tc.left[cur];
+ }
+ if (!st2.isEmpty()) {
+ final int node = st2.pop();
+ inorder.add(node);
+ cur = tc.right[node];
+ }
+ }
+
+ // postorder using modified preorder (root-right-left) then reverse
+ final List postTmp = new ArrayList<>();
+ final Deque st3 = new ArrayDeque<>();
+ if (root != 0) st3.push(root);
+ while (!st3.isEmpty()) {
+ final int u = st3.pop();
+ postTmp.add(u);
+ final int l = tc.left[u];
+ final int r = tc.right[u];
+ if (l != 0) st3.push(l);
+ if (r != 0) st3.push(r);
+ }
+ final List postorder = new ArrayList<>(postTmp.size());
+ for (int i = postTmp.size() - 1; i >= 0; i--) postorder.add(postTmp.get(i));
+
+ // join lines
+ outLines.add(joinInts(preorder));
+ outLines.add(joinInts(inorder));
+ outLines.add(joinInts(postorder));
+ }
+ return outLines;
+ }
+
+ // output: print each line and ensure newline after each
+ public static void output(final List lines) {
+ final StringBuilder sb = new StringBuilder();
+ for (final var line : lines) {
+ sb.append(line);
+ sb.append('\n');
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(final String[] args) {
+ output(cal(reader()));
+ }
+
+ private static String joinInts(final List arr) {
+ final StringJoiner sj = new StringJoiner(" ");
+ for (final var v : arr) {
+ sj.add(String.valueOf(v));
+ }
+ return sj.toString();
+ }
+
+ // fast reader
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ public String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ final String line = br.readLine();
+ if (line == null) return "";
+ st = new StringTokenizer(line);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ public int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/lab_6_1153/test/MainTest.java b/2018fall/lab_6/lab_6_1153/test/MainTest.java
new file mode 100644
index 0000000..625f5ef
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1153/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/pom.xml b/2018fall/lab_6/pom.xml
index f9c2825..ce98c5f 100644
--- a/2018fall/lab_6/pom.xml
+++ b/2018fall/lab_6/pom.xml
@@ -18,6 +18,7 @@
${project.groupId}.${project.artifactId}
lab_6_1152
+ lab_6_1153
lab_6_1158
From 95646e0d24fe1b8d0822f9f41a6292772c6e4dab Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:18:02 +0000
Subject: [PATCH 20/46] feat: add 2018fall/lab6-1154
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_6/lab_6_1154/README.md | 74 ++++++++++
2018fall/lab_6/lab_6_1154/pom.xml | 22 +++
.../lab_6/lab_6_1154/resources/01.data.in | 6 +
.../lab_6/lab_6_1154/resources/01.data.out | 1 +
2018fall/lab_6/lab_6_1154/src/Main.java | 127 ++++++++++++++++++
2018fall/lab_6/lab_6_1154/test/MainTest.java | 46 +++++++
2018fall/lab_6/pom.xml | 1 +
7 files changed, 277 insertions(+)
create mode 100644 2018fall/lab_6/lab_6_1154/README.md
create mode 100644 2018fall/lab_6/lab_6_1154/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1154/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1154/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1154/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1154/test/MainTest.java
diff --git a/2018fall/lab_6/lab_6_1154/README.md b/2018fall/lab_6/lab_6_1154/README.md
new file mode 100644
index 0000000..b367e44
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1154/README.md
@@ -0,0 +1,74 @@
+## Description
+
+There is a set with size n initially, and there are q operations, each operation will be one of the following cases:
+
+Add x: add x to this set.
+
+Delete: delete the minimum element of the set.
+
+Query: print the minimum element of the set.
+
+### Input
+
+The first line will be an integer T, which is the number of test cases. (1 <= T <= 10).
+
+For each test case, the first line will be an integer n (1 <= n <= 10^5), then the second line will be n integers ai (1 <= ai <= 10^9), they make up the initial set. The third line will be an integer q (1 <= q <= 10^5), it means the number of operations. Then followed by q lines, each line will be one of the following cases:
+
+1 x: Add x (1 <= x <= 10^9).
+
+2: Delete.
+
+3: Query.
+
+### Output
+
+For each "Query", print the minimum element of the set in a line.
+
+### Sample Input
+
+```log
+1
+2
+2 3
+2
+1 2
+3
+```
+
+### Sample Output
+
+```log
+2
+```
+
+## 解法
+
+### 算法思路
+
+- 输入与预处理
+ - 使用快读 Reader 解析输入, 先读 T, 每个用例读 n 和序列 a[0..n-1], 然后读 q 及接下來的 q 个操作.
+ - 在 reader() 中对输入约束加入 assert 检查, 例如 assert ((1 <= n) && (n <= 100000));
+
+- 数据结构
+ - 使用 Java 的 PriorityQueue 作为最小堆来维护集合的当前元素, 支持 O(log N) 的插入与删除最小值操作.
+ - 元素允许重复; 删除操作按堆的 poll() 行为移除当前最小值.
+
+- 操作处理
+ - type 1 x: 调用 pq.add(x) 插入元素.
+ - type 2: 如果 pq 非空则调用 pq.poll() 删除最小元素, 否则忽略.
+ - type 3: 如果 pq 非空则输出 pq.peek() 作为当前最小值, 否则输出 -1.
+
+- 读-处理-输出分离
+ - reader() 负责解析并构建 TestCase 数据结构.
+ - cal() 接受 TestCase 列表, 对每个用例执行操作并将所有 Query 的结果收集为字符串行列表.
+ - output() 负责最终输出, 使用 StringBuilder 聚合并用 System.out.print 一次性输出, 每行以 '\n' 结尾.
+
+- 复杂度分析
+ - 时间复杂度: 每个测试用例为 O((n + q) log N) (N 为堆中元素数量的上界), 总体可在题目约束范围内运行.
+ - 空间复杂度: O(N) 用于堆和存储输入操作.
+
+- 边界与鲁棒性
+ - 当集合为空时, Delete 操作为无操作, Query 返回 -1.
+ - 使用 assert 可在开发/测试阶段尽早发现不合法输入.
+
+实现细节请参见 src/Main.java 中的 reader, cal, output 的具体代码。
diff --git a/2018fall/lab_6/lab_6_1154/pom.xml b/2018fall/lab_6/lab_6_1154/pom.xml
new file mode 100644
index 0000000..b4faa52
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1154/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1154
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1154/resources/01.data.in b/2018fall/lab_6/lab_6_1154/resources/01.data.in
new file mode 100644
index 0000000..1a66de3
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1154/resources/01.data.in
@@ -0,0 +1,6 @@
+1
+2
+2 3
+2
+1 2
+3
diff --git a/2018fall/lab_6/lab_6_1154/resources/01.data.out b/2018fall/lab_6/lab_6_1154/resources/01.data.out
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1154/resources/01.data.out
@@ -0,0 +1 @@
+2
diff --git a/2018fall/lab_6/lab_6_1154/src/Main.java b/2018fall/lab_6/lab_6_1154/src/Main.java
new file mode 100644
index 0000000..d84a1f1
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1154/src/Main.java
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ public final int n;
+ public final int[] a;
+ public final int q;
+ public final int[] type;
+ public final int[] val;
+
+ public TestCase(int n, int[] a, int q, int[] type, int[] val) {
+ this.n = n;
+ this.a = a;
+ this.q = q;
+ this.type = type;
+ this.val = val;
+ }
+ }
+
+ // reader: parse input into TestCase objects
+ public static List reader() {
+ final var in = new Reader();
+ final int T = in.nextInt();
+ assert ((1 <= T) && (T <= 10));
+ final List tests = new ArrayList<>(T);
+ for (int tc = 0; tc < T; tc++) {
+ final int n = in.nextInt();
+ assert ((1 <= n) && (n <= 100000));
+ final int[] a = new int[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = in.nextInt();
+ }
+ final int q = in.nextInt();
+ assert ((1 <= q) && (q <= 100000));
+ final int[] type = new int[q];
+ final int[] val = new int[q];
+ for (int i = 0; i < q; i++) {
+ final int t = in.nextInt();
+ type[i] = t;
+ if (t == 1) {
+ final int x = in.nextInt();
+ val[i] = x;
+ } else {
+ val[i] = 0;
+ }
+ }
+ tests.add(new TestCase(n, a, q, type, val));
+ }
+ return tests;
+ }
+
+ // cal: execute operations and collect outputs for queries
+ public static List cal(final List inputs) {
+ final List outLines = new ArrayList<>();
+ for (final var tc : inputs) {
+ final PriorityQueue pq = new PriorityQueue<>();
+ for (final var x : tc.a) pq.add(x);
+ for (int i = 0; i < tc.q; i++) {
+ final int t = tc.type[i];
+ if (t == 1) {
+ pq.add(tc.val[i]);
+ } else if (t == 2) {
+ if (!pq.isEmpty()) pq.poll();
+ } else if (t == 3) {
+ if (pq.isEmpty()) {
+ outLines.add(String.valueOf(-1));
+ } else {
+ outLines.add(String.valueOf(pq.peek()));
+ }
+ }
+ }
+ }
+ return outLines;
+ }
+
+ // output: print each line and ensure newline after each
+ public static void output(final List lines) {
+ final StringBuilder sb = new StringBuilder();
+ for (final var line : lines) {
+ sb.append(line);
+ sb.append('\n');
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(final String[] args) {
+ output(cal(reader()));
+ }
+
+ // fast reader
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ public String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ final String line = br.readLine();
+ if (line == null) return "";
+ st = new StringTokenizer(line);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ public int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/lab_6_1154/test/MainTest.java b/2018fall/lab_6/lab_6_1154/test/MainTest.java
new file mode 100644
index 0000000..625f5ef
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1154/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/pom.xml b/2018fall/lab_6/pom.xml
index ce98c5f..5fc5009 100644
--- a/2018fall/lab_6/pom.xml
+++ b/2018fall/lab_6/pom.xml
@@ -19,6 +19,7 @@
lab_6_1152
lab_6_1153
+ lab_6_1154
lab_6_1158
From 18cbed42da8252abd5bc68be22f5cf406df1ddd2 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:22:42 +0000
Subject: [PATCH 21/46] feat: add 2018fal/lab6-1155
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_6/lab_6_1155/README.md | 67 ++++++++
2018fall/lab_6/lab_6_1155/pom.xml | 22 +++
.../lab_6/lab_6_1155/resources/01.data.in | 9 ++
.../lab_6/lab_6_1155/resources/01.data.out | 1 +
2018fall/lab_6/lab_6_1155/src/Main.java | 149 ++++++++++++++++++
2018fall/lab_6/lab_6_1155/test/MainTest.java | 46 ++++++
2018fall/lab_6/pom.xml | 1 +
7 files changed, 295 insertions(+)
create mode 100644 2018fall/lab_6/lab_6_1155/README.md
create mode 100644 2018fall/lab_6/lab_6_1155/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1155/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1155/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1155/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1155/test/MainTest.java
diff --git a/2018fall/lab_6/lab_6_1155/README.md b/2018fall/lab_6/lab_6_1155/README.md
new file mode 100644
index 0000000..f9dc0fb
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1155/README.md
@@ -0,0 +1,67 @@
+## Description
+
+Write a program to print the longest distance between two nodes of the given tree.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 10), which is the number of test cases.
+
+For each test data:
+
+The first line contains one integer N (1 <= N <= 10^5) - the number of nodes.
+
+Each of the next N - 1 lines contain two integers a and b, which means there is an edge between node a and b.
+
+### Output
+
+For each case, please print the longest distance between any two nodes of the given tree.
+
+### Sample Input
+
+```log
+1
+8
+1 4
+1 3
+4 2
+2 7
+3 5
+3 6
+6 8
+```
+
+### Sample Output
+
+```log
+6
+```
+
+## 解法
+
+### 算法思路
+
+- 输入与预处理
+ - 使用快读 Reader 解析输入, 先读 T, 每个用例读 n, 接着读 n-1 条无向边.
+ - 在 reader() 中对输入约束加入 assert 检查, 例如 assert ((1 <= n) && (n <= 100000));
+
+- 建图
+ - 使用 1-based 的邻接表存储无向图, 对于每条边 a b 同时在 g[a] 和 g[b] 中添加对方.
+
+- 求直径的方法(两次 BFS)
+ - 任意选择一个起点 s (例如 1), 用 BFS 计算从 s 到所有节点的距离, 找到距离最远的点 u.
+ - 从 u 再次做 BFS, 最大距离即为树的直径.
+ - BFS 使用 ArrayDeque 做队列, 迭代实现, 避免递归.
+
+- 复杂度
+ - 时间复杂度: O(n) 每次 BFS, 共 O(n) 两次, 每个用例总体 O(n).
+ - 空间复杂度: O(n) 用于邻接表和距离数组.
+
+- 边界与鲁棒性
+ - 当 n == 1 时, 直径为 0.
+ - 通过 assert 在开发/测试阶段尽早发现非法输入.
+
+- 设计与实现原则
+ - 遵循读-处理-输出分离: reader() 解析并构建 TestCase, cal() 负责计算直径并返回输出行, output() 负责最终打印并保证每行以 '\n' 结尾.
+ - 使用迭代 BFS 保证在大输入下的稳定性和性能.
+
+实现细节请参见 src/Main.java 中的 reader, cal, output 的具体代码。
diff --git a/2018fall/lab_6/lab_6_1155/pom.xml b/2018fall/lab_6/lab_6_1155/pom.xml
new file mode 100644
index 0000000..20b19e4
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1155/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1155
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1155/resources/01.data.in b/2018fall/lab_6/lab_6_1155/resources/01.data.in
new file mode 100644
index 0000000..669d001
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1155/resources/01.data.in
@@ -0,0 +1,9 @@
+1
+8
+1 4
+1 3
+4 2
+2 7
+3 5
+3 6
+6 8
diff --git a/2018fall/lab_6/lab_6_1155/resources/01.data.out b/2018fall/lab_6/lab_6_1155/resources/01.data.out
new file mode 100644
index 0000000..1e8b314
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1155/resources/01.data.out
@@ -0,0 +1 @@
+6
diff --git a/2018fall/lab_6/lab_6_1155/src/Main.java b/2018fall/lab_6/lab_6_1155/src/Main.java
new file mode 100644
index 0000000..5b44927
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1155/src/Main.java
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ public final int n;
+ public final int[][] edges; // m x 2, edges
+
+ public TestCase(int n, int[][] edges) {
+ this.n = n;
+ this.edges = edges;
+ }
+ }
+
+ // reader: parse input into TestCase objects
+ public static List reader() {
+ final var in = new Reader();
+ final int T = in.nextInt();
+ assert ((1 <= T) && (T <= 10));
+ final List tests = new ArrayList<>(T);
+ for (int tc = 0; tc < T; tc++) {
+ final int n = in.nextInt();
+ assert ((1 <= n) && (n <= 100000));
+ final int m = n - 1;
+ final int[][] edges = new int[m][2];
+ for (int i = 0; i < m; i++) {
+ final int a = in.nextInt();
+ final int b = in.nextInt();
+ assert ((1 <= a) && (a <= n));
+ assert ((1 <= b) && (b <= n));
+ edges[i][0] = a;
+ edges[i][1] = b;
+ }
+ tests.add(new TestCase(n, edges));
+ }
+ return tests;
+ }
+
+ // cal: compute diameter for each test case and return output lines
+ public static List cal(final List inputs) {
+ final List outLines = new ArrayList<>();
+ for (final var tc : inputs) {
+ final int n = tc.n;
+ if (n == 1) {
+ outLines.add("0");
+ continue;
+ }
+ // build adjacency
+ final List> g = new ArrayList<>(n + 1);
+ g.add(new ArrayList<>()); // index 0 dummy
+ for (int i = 1; i <= n; i++) g.add(new ArrayList<>());
+ for (final var e : tc.edges) {
+ final int u = e[0];
+ final int v = e[1];
+ g.get(u).add(v);
+ g.get(v).add(u);
+ }
+ // first BFS from node 1 (or any existing node) to find farthest
+ final int start = 1;
+ final int[] res1 = bfsFar(g, start);
+ int far = start;
+ int maxd = -1;
+ for (int i = 1; i <= n; i++) {
+ if (res1[i] > maxd) {
+ maxd = res1[i];
+ far = i;
+ }
+ }
+ // second BFS from far to get diameter
+ final int[] res2 = bfsFar(g, far);
+ int diam = 0;
+ for (int i = 1; i <= n; i++) if (res2[i] > diam) diam = res2[i];
+ outLines.add(String.valueOf(diam));
+ }
+ return outLines;
+ }
+
+ // BFS that returns distances from s (1-based indexing)
+ private static int[] bfsFar(final List> g, final int s) {
+ final int n = g.size() - 1;
+ final int[] dist = new int[n + 1];
+ for (int i = 1; i <= n; i++) dist[i] = -1;
+ final Deque dq = new ArrayDeque<>();
+ dist[s] = 0;
+ dq.addLast(s);
+ while (!dq.isEmpty()) {
+ final int u = dq.removeFirst();
+ for (final var v : g.get(u)) {
+ if (dist[v] == -1) {
+ dist[v] = dist[u] + 1;
+ dq.addLast(v);
+ }
+ }
+ }
+ return dist;
+ }
+
+ // output: print each line and ensure newline after each
+ public static void output(final List lines) {
+ final StringBuilder sb = new StringBuilder();
+ for (final var line : lines) {
+ sb.append(line);
+ sb.append('\n');
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(final String[] args) {
+ output(cal(reader()));
+ }
+
+ // fast reader
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ public String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ final String line = br.readLine();
+ if (line == null) return "";
+ st = new StringTokenizer(line);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ public int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/lab_6_1155/test/MainTest.java b/2018fall/lab_6/lab_6_1155/test/MainTest.java
new file mode 100644
index 0000000..625f5ef
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1155/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/pom.xml b/2018fall/lab_6/pom.xml
index 5fc5009..4222e54 100644
--- a/2018fall/lab_6/pom.xml
+++ b/2018fall/lab_6/pom.xml
@@ -20,6 +20,7 @@
lab_6_1152
lab_6_1153
lab_6_1154
+ lab_6_1155
lab_6_1158
From 809853043069dd6c621d82c0d711f5104476c038 Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:34:27 +0000
Subject: [PATCH 22/46] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=202018fall/lab?=
=?UTF-8?q?6-1156?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_6/README.md | 8 +-
2018fall/lab_6/lab_6_1156/README.md | 111 ++++++++++++
2018fall/lab_6/lab_6_1156/pom.xml | 22 +++
.../lab_6/lab_6_1156/resources/01.data.in | 13 ++
.../lab_6/lab_6_1156/resources/01.data.out | 6 +
2018fall/lab_6/lab_6_1156/src/Main.java | 159 ++++++++++++++++++
2018fall/lab_6/lab_6_1156/test/MainTest.java | 46 +++++
2018fall/lab_6/pom.xml | 1 +
AGENTS.md | 2 +
9 files changed, 364 insertions(+), 4 deletions(-)
create mode 100644 2018fall/lab_6/lab_6_1156/README.md
create mode 100644 2018fall/lab_6/lab_6_1156/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1156/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1156/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1156/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1156/test/MainTest.java
diff --git a/2018fall/lab_6/README.md b/2018fall/lab_6/README.md
index 6a2fc71..6ac5c0d 100644
--- a/2018fall/lab_6/README.md
+++ b/2018fall/lab_6/README.md
@@ -19,10 +19,10 @@ Read the samples carefully can help you understand the problem.
## Stack And Queue
+ [x] problem A: lab_6_1152
-+ [ ] problem B: lab_6_1153
-+ [ ] problem C: lab_6_1154
-+ [ ] problem D: lab_6_1155
-+ [ ] problem E: lab_6_1156
++ [x] problem B: lab_6_1153
++ [x] problem C: lab_6_1154
++ [x] problem D: lab_6_1155
++ [x] problem E: lab_6_1156
+ [ ] problem F: lab_6_1157
+ [x] problem G: lab_6_1158
diff --git a/2018fall/lab_6/lab_6_1156/README.md b/2018fall/lab_6/lab_6_1156/README.md
new file mode 100644
index 0000000..6c3684f
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1156/README.md
@@ -0,0 +1,111 @@
+## Description
+
+The capacity of Hong’s pocket is so small that it can only contain $M$ gifts.
+
+Considering the diversity of his gifts, Hong would not buy two of the same kind.
+
+Hong will visit $N$ shops one by one along the shopping street.
+
+There is **ONLY ONE** type of gift sold in each shop. However, he has such a poor memory that he can’t remember how many shops sell gift $K$.
+
+So, he will write a number L on each gift after buying it, to indicate how many shops selling gift $K$.
+
+In Hong’s opinion, the smaller the number $L$ is, the better the gift is.
+
+When Hong stops in a shop which sells gift $K$ , there are three situations he might come across.
+
+1. If there is no gift $K$ in his pocket and he still has some place for it, he will buy it without hesitation.
+
+Before putting it into the pocket, he will write down the number ‘1’ on the gift to indicate that he has only seen one shop selling it.
+
+2. If there is a gift $K$ already in his pocket, he will just add L by one, which means that there are L+1 shops selling gift $K$
+
+3. If there is no gift $K$ in his pocket and the pocket is full, he would consider that there is no shop selling gift $K$ (because he cannot remember whether he has met gift $K$), so he will have to discard one gift in his pocket to release a place for the gift $K$
+But it will refer to the following rules to determine which gifts to be discarded:
+
+He chooses the gift that has the biggest number L on it.
+
+If several gifts have the same biggest number L, he will discard the one which has been put into the pocket at the earliest time.
+
+After discarding the gift, he will put gift $K$ into his pocket and write number ‘1’ on gift $K$
+
+Now, your task is to write a program to record the number of these gifts which have been discarded by Hong.
+
+### Input
+
+The first line will be an integer T(1≤T≤10) , which is the number of test cases.
+
+For each test data:
+
+The first line has two positive integers $M$
+
+and $N$ ($M$≤50000,$N$≤100000) where $M$ (the capacity of pocket) shows how many gifts it can take, and $N$ is the number of shops in the street. The second line has $N$ positive integers $K$i($K$i<220,i=1,2,⋯,$N$)
+
+indicating the type of gift sold in the i-th shop.
+
+### Output
+
+For each test case you should output one integer, the number of discarded gifts as indicated in the sample output.
+
+### Sample Input
+
+```log
+6
+3 5
+1 2 3 2 4
+2 4
+1 2 2 1
+2 6
+1 2 2 1 1024 1
+2 10
+1 2 3 2 4 2 3 6 7 8
+2 1
+1048575
+6 16
+10 1 2 3 4 5 6 1 2 3 6 5 4 10 1 6
+```
+
+### Sample Output
+
+```log
+1
+0
+2
+7
+0
+3
+```
+
+## 解法
+
+### 算法思路
+
+- 总体框架
+ - 遵循读-处理-输出分离:`reader()` 负责解析输入并构建测试用例数据,`cal()` 负责模拟购物过程并计算被丢弃礼物的数量,`output()` 负责最终打印结果。
+
+- 数据结构
+ - 使用 `HashMap` 保存当前口袋中每种礼物的信息(包含礼物 id、标签 L、入袋时间 time)。
+ - 使用 `TreeSet` 维护口袋中条目的排序,以便快速找到要丢弃的礼物。比较器按以下优先级排序:
+ 1) L 值较大者优先(即 L 从大到小);
+ 2) 若 L 相同,则按入袋时间较早者优先被丢弃(time 从小到大);
+ 3) 若仍相同,则按 id 作为稳定性保证比较。
+ - 采用先从 `TreeSet` 中 remove 再修改 Entry 再 add 的方式更新条目,保证集合一致性。
+
+- 模拟规则
+ - 遍历商店序列,对每个礼物 K 执行:
+ - 若 K 已在口袋中,先从 `TreeSet` 删除对应 Entry,将 L++,再重新加入 `TreeSet`。
+ - 若 K 不在口袋中且口袋未满,直接创建 Entry(L=1, time=当前计时器) 并加入 `HashMap` 与 `TreeSet`,计时器自增。
+ - 若 K 不在口袋中且口袋已满,先从 `TreeSet` 中取出第一个元素(根据比较器为应被丢弃的礼物),从 `HashMap` 中移除并将丢弃计数加一,然后插入新礼物的 Entry(L=1, time=计时器),再将计时器自增。
+
+- 特殊与边界情况
+ - 当 M == 0 时,按实现当前语义不存放任何礼物,丢弃计数返回 0。如需按题目另一种解释调整行为,可修改该分支逻辑。
+ - 输入约束在 `reader()` 中通过 `assert` 检查,例如 `assert ((1 <= N) && (N <= 100000));`,在开发/测试阶段可早期发现不合法输入。
+
+- 复杂度分析
+ - 每步插入/删除/更新 `TreeSet` 和 `HashMap` 的复杂度为 O(log M) 或 O(1),总体时间复杂度为 O(N log M)(N 为商店数,M 为口袋容量)。
+ - 空间复杂度为 O(M) 额外内存用于口袋管理。
+
+- 设计原则
+ - 避免递归和全局可变混乱,采用局部封装的 `Entry` 对象和明确的集合操作步骤,保证数据结构一致性与可测试性。
+
+实现细节请参见 `src/Main.java` 中的 `reader`, `cal`, `output` 具体实现。
diff --git a/2018fall/lab_6/lab_6_1156/pom.xml b/2018fall/lab_6/lab_6_1156/pom.xml
new file mode 100644
index 0000000..318a94a
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1156/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1156
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1156/resources/01.data.in b/2018fall/lab_6/lab_6_1156/resources/01.data.in
new file mode 100644
index 0000000..4a273d4
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1156/resources/01.data.in
@@ -0,0 +1,13 @@
+6
+3 5
+1 2 3 2 4
+2 4
+1 2 2 1
+2 6
+1 2 2 1 1024 1
+2 10
+1 2 3 2 4 2 3 6 7 8
+2 1
+1048575
+6 16
+10 1 2 3 4 5 6 1 2 3 6 5 4 10 1 6
diff --git a/2018fall/lab_6/lab_6_1156/resources/01.data.out b/2018fall/lab_6/lab_6_1156/resources/01.data.out
new file mode 100644
index 0000000..f4e4f6c
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1156/resources/01.data.out
@@ -0,0 +1,6 @@
+1
+0
+2
+7
+0
+3
diff --git a/2018fall/lab_6/lab_6_1156/src/Main.java b/2018fall/lab_6/lab_6_1156/src/Main.java
new file mode 100644
index 0000000..3960e5c
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1156/src/Main.java
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+public final class Main {
+
+ public static final class TestCase {
+ public final int m;
+ public final int n;
+ public final int[] shops;
+
+ public TestCase(int m, int n, int[] shops) {
+ this.m = m;
+ this.n = n;
+ this.shops = shops;
+ }
+ }
+
+ // reader: parse input into TestCase objects
+ public static List reader() {
+ final var in = new Reader();
+ final int T = in.nextInt();
+ assert ((1 <= T) && (T <= 10));
+ final List tests = new ArrayList<>(T);
+ for (int tc = 0; tc < T; tc++) {
+ final int M = in.nextInt();
+ final int N = in.nextInt();
+ assert ((0 <= M) && (M <= 50000));
+ assert ((1 <= N) && (N <= 100000));
+ final int[] shops = new int[N];
+ for (int i = 0; i < N; i++) shops[i] = in.nextInt();
+ tests.add(new TestCase(M, N, shops));
+ }
+ return tests;
+ }
+
+ // cal: simulate and return outputs
+ public static List cal(final List inputs) {
+ final List out = new ArrayList<>();
+ for (final var tc : inputs) {
+ final int M = tc.m;
+ final int N = tc.n;
+ final int[] shops = tc.shops;
+ if (M == 0) {
+ // pocket cannot hold anything, each new distinct gift causes a discard if any space is needed
+ // but since he cannot buy at all, every time sees a gift not in pocket and pocket full (M==0),
+ // he would discard none from pocket because pocket empty—interpretation: pocket size 0 means never store, so discarded count 0.
+ // Following problem intent, treat M==0 as never storing so discard count 0.
+ out.add("0");
+ continue;
+ }
+
+ // pocket entry
+ final class Entry {
+ final int id;
+ int L;
+ final long time;
+
+ Entry(int id, int L, long time) {
+ this.id = id;
+ this.L = L;
+ this.time = time;
+ }
+ }
+
+ final Map inPocket = new HashMap<>();
+ final TreeSet set = new TreeSet<>((a, b) -> {
+ if (a.L != b.L) return Integer.compare(b.L, a.L); // larger L first
+ if (a.time != b.time) return Long.compare(a.time, b.time); // earlier time first
+ return Integer.compare(a.id, b.id);
+ });
+
+ long timer = 0L;
+ int discarded = 0;
+
+ for (int i = 0; i < N; i++) {
+ final int k = shops[i];
+ final Entry cur = inPocket.get(k);
+ if (cur != null) {
+ // increment L
+ set.remove(cur);
+ cur.L = cur.L + 1;
+ set.add(cur);
+ } else {
+ // not in pocket
+ if (inPocket.size() < M) {
+ final Entry e = new Entry(k, 1, timer++);
+ inPocket.put(k, e);
+ set.add(e);
+ } else {
+ // pocket full: evict one by rule
+ final Entry victim = set.pollFirst();
+ if (victim != null) {
+ inPocket.remove(victim.id);
+ discarded++;
+ }
+ // insert new gift
+ final Entry e = new Entry(k, 1, timer++);
+ inPocket.put(k, e);
+ set.add(e);
+ }
+ }
+ }
+ out.add(String.valueOf(discarded));
+ }
+ return out;
+ }
+
+ // output
+ public static void output(final List lines) {
+ final StringBuilder sb = new StringBuilder();
+ for (final var line : lines) {
+ sb.append(line);
+ sb.append('\n');
+ }
+ System.out.print(sb);
+ }
+
+ public static void main(final String[] args) {
+ output(cal(reader()));
+ }
+
+ // fast reader
+ public static final class Reader {
+ private final BufferedReader br;
+ private StringTokenizer st;
+
+ public Reader() {
+ br = new BufferedReader(new InputStreamReader(System.in));
+ }
+
+ public String next() {
+ while (st == null || !st.hasMoreElements()) {
+ try {
+ final String line = br.readLine();
+ if (line == null) return "";
+ st = new StringTokenizer(line);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return st.nextToken();
+ }
+
+ public int nextInt() {
+ return Integer.parseInt(next());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/lab_6_1156/test/MainTest.java b/2018fall/lab_6/lab_6_1156/test/MainTest.java
new file mode 100644
index 0000000..625f5ef
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1156/test/MainTest.java
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import tests.Pair;
+import tests.Redirect;
+
+
+import java.io.*;
+
+@Slf4j
+public final class MainTest {
+ private static final String DATA_PATH = "resources/";
+ private static final long begin_time = System.currentTimeMillis();
+
+ @AfterAll
+ public static void last_one() throws IOException {
+ log.info("cost {} ms\n", System.currentTimeMillis() - begin_time);
+ }
+
+ @BeforeEach
+ public void beforeEach(TestInfo testInfo) {
+ log.info("{} begin", testInfo.getDisplayName());
+ }
+
+ @AfterEach
+ public void afterEach(TestInfo testInfo) {
+ log.info("{} end", testInfo.getDisplayName());
+ }
+
+ @Test
+ public void test_2() throws IOException {
+ try (Redirect redirect = Redirect.from(DATA_PATH,"01.data.in", "01.test.out")){
+ Main.output(Main.cal(Main.reader()));
+ final Pair p = redirect.compare_double("01.data.out", "01.test.out");
+ Assertions.assertEquals(p.getFirst().length(), p.getSecond().length());
+ Assertions.assertEquals(p.getFirst(), p.getSecond());
+ }
+ }
+
+}
diff --git a/2018fall/lab_6/pom.xml b/2018fall/lab_6/pom.xml
index 4222e54..eea88fa 100644
--- a/2018fall/lab_6/pom.xml
+++ b/2018fall/lab_6/pom.xml
@@ -21,6 +21,7 @@
lab_6_1153
lab_6_1154
lab_6_1155
+ lab_6_1156
lab_6_1158
diff --git a/AGENTS.md b/AGENTS.md
index 616895d..e917703 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -40,3 +40,5 @@
+ example: `assert ((0 <= x) && (x <= 100));`
3. 执行测试的命令行操作: `mvn -q -pl .\2018fall\lab_{}\lab_{}_{}\ -am test`
+
+4. 在 README.md 中说明死颅
From 9a44bea93c18462bd9f2ebe8103cb8a5d1b6ac1e Mon Sep 17 00:00:00 2001
From: Certseeds <51754303+Certseeds@users.noreply.github.com>
Date: Sun, 21 Sep 2025 12:42:05 +0000
Subject: [PATCH 23/46] feat: add 2018fall/lab6-1157
Signed-off-by: Certseeds <51754303+Certseeds@users.noreply.github.com>
---
2018fall/lab_6/README.md | 2 +-
2018fall/lab_6/lab_6_1157/README.md | 68 +++++++++
2018fall/lab_6/lab_6_1157/pom.xml | 22 +++
.../lab_6/lab_6_1157/resources/01.data.in | 4 +
.../lab_6/lab_6_1157/resources/01.data.out | 1 +
2018fall/lab_6/lab_6_1157/src/Main.java | 129 ++++++++++++++++++
2018fall/lab_6/lab_6_1157/test/MainTest.java | 46 +++++++
2018fall/lab_6/pom.xml | 1 +
8 files changed, 272 insertions(+), 1 deletion(-)
create mode 100644 2018fall/lab_6/lab_6_1157/README.md
create mode 100644 2018fall/lab_6/lab_6_1157/pom.xml
create mode 100644 2018fall/lab_6/lab_6_1157/resources/01.data.in
create mode 100644 2018fall/lab_6/lab_6_1157/resources/01.data.out
create mode 100644 2018fall/lab_6/lab_6_1157/src/Main.java
create mode 100644 2018fall/lab_6/lab_6_1157/test/MainTest.java
diff --git a/2018fall/lab_6/README.md b/2018fall/lab_6/README.md
index 6ac5c0d..a181382 100644
--- a/2018fall/lab_6/README.md
+++ b/2018fall/lab_6/README.md
@@ -23,7 +23,7 @@ Read the samples carefully can help you understand the problem.
+ [x] problem C: lab_6_1154
+ [x] problem D: lab_6_1155
+ [x] problem E: lab_6_1156
-+ [ ] problem F: lab_6_1157
++ [x] problem F: lab_6_1157
+ [x] problem G: lab_6_1158
## 总体评价
diff --git a/2018fall/lab_6/lab_6_1157/README.md b/2018fall/lab_6/lab_6_1157/README.md
new file mode 100644
index 0000000..8ff4b11
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1157/README.md
@@ -0,0 +1,68 @@
+## Description
+
+Hong likes game very much. He wants to play a game with you.
+
+There is a tree with N nodes. Node 1 is the root. Each node is colored black or white.
+
+Each turn, the player should choose a black node and change it to white. After that, he can choose its any number of the proper ancestors and change their color. The one who cannot find a black node at the tree in his turn, he lose the game.
+
+Hong is good at the game, so he let you take the first turn. Hong will always find the optimal solution. He wants to know if you can win the game.
+
+### Input
+
+The first line will be an integer T (1 <= T <= 100), which is the number of test cases.
+
+For each test data:
+
+The first line contains one integer N (1 <= N <= 10000) - the number of the nodes.
+
+The second line contains N integers w1 ... wn in {0, 1}, wi = 1 means node i is black. Otherwise node i is white.
+
+Each of the next N - 1 lines contain two integers a and b, which means there is an edge between node a and b.
+
+### Output
+
+For each test case, if you can win, print "YES"; otherwise, print "NO".
+
+### Sample Input
+
+```log
+1
+2
+1 0
+1 2
+```
+
+### Sample Output
+
+```log
+YES
+```
+
+## 解法
+
+### 算法思路
+
+- 问题类型
+ - 这是一个轮流操作的零和博弈问题, 可以用 Sprague-Grundy 定理将每个局部子博弈转化为一个 Grundy 值, 并用 xor 来合并整体局面.
+
+- 状态建模
+ - 将以根节点为基础的子树视为一个局部博弈单元. 定义 g(u) 为以节点 u 为根的子树在当前颜色分布下的 Grundy 值.
+
+- 转移与计算
+ - 对于节点 u, 枚举所有合法的一步操作(选择某个黑点并且可选地改变若干祖先颜色)后得到的若干子局面, 计算这些子局面的 Grundy 值集合 S(u), 则 g(u) = mex(S(u)).
+ - 为了高效得到 S(u), 采用自底向上的 DFS: 先计算所有孩子的 g 值, 然后根据题目的合法操作规则构造 S(u) 并计算 mex。
+ - 全局局面即各个独立分量或以根为基准的合并, 全局 Grundy = 异或(所有 g(u) 的合适组合), 若全局 Grundy != 0 则先手必胜, 否则后手必胜。
+
+- 复杂度
+ - 通过一次 DFS 自底向上计算每个节点的 Grundy 值, 每个节点的处理可以在与其子节点数量成线性的时间内完成, 因此总体时间复杂度为 O(N). 空间复杂度为 O(N) 用于邻接表和递归栈/辅助数组.
+
+- 边界与鲁棒性
+ - 当树只有 1 个节点时, 直接判断该节点颜色即可得出结果.
+ - 在 `reader()` 中加入 assert 检查输入范围, 例如 assert ((1 <= N) && (N <= 10000)); 以便在非法输入时尽早报错.
+
+实现提示
+- 实际实现时, 重点是正确、完整地枚举一步操作后子局面的 Grundy 值集合 S(u) 并高效计算 mex, 可用布尔数组或哈希集合记录已出现的值来计算 mex.
+- 最终输出: 若整体 Grundy != 0 则打印 "YES" 否则打印 "NO".
+
+具体实现请参见 `src/Main.java` 中的 reader, cal, output 的代码实现。
diff --git a/2018fall/lab_6/lab_6_1157/pom.xml b/2018fall/lab_6/lab_6_1157/pom.xml
new file mode 100644
index 0000000..0f48136
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1157/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ nanoseeds.algorithm-template.2018fall
+ lab_6
+ ${revision}
+ ./../pom.xml
+
+ nanoseeds.algorithm-template.2018fall.lab6
+ lab_6_1157
+ ${revision}
+ ${project.groupId}.${project.artifactId}
+ ${project.groupId}.${project.artifactId}
+
+
+ ${project.basedir}/src
+ ${project.basedir}/test
+
+
diff --git a/2018fall/lab_6/lab_6_1157/resources/01.data.in b/2018fall/lab_6/lab_6_1157/resources/01.data.in
new file mode 100644
index 0000000..66a455d
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1157/resources/01.data.in
@@ -0,0 +1,4 @@
+1
+2
+1 0
+1 2
diff --git a/2018fall/lab_6/lab_6_1157/resources/01.data.out b/2018fall/lab_6/lab_6_1157/resources/01.data.out
new file mode 100644
index 0000000..f033a50
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1157/resources/01.data.out
@@ -0,0 +1 @@
+YES
diff --git a/2018fall/lab_6/lab_6_1157/src/Main.java b/2018fall/lab_6/lab_6_1157/src/Main.java
new file mode 100644
index 0000000..313d769
--- /dev/null
+++ b/2018fall/lab_6/lab_6_1157/src/Main.java
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-FileCopyrightText: 2018-2025 nanoseeds
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public final class Main {
+
+ public static final class TestCase {
+ public final int n;
+ public final int[] w;
+ public final int[][] edges;
+
+ public TestCase(int n, int[] w, int[][] edges) {
+ this.n = n;
+ this.w = w;
+ this.edges = edges;
+ }
+ }
+
+ // reader: parse input into TestCase objects
+ public static List reader() {
+ final var in = new Reader();
+ final int T = in.nextInt();
+ assert ((1 <= T) && (T <= 100));
+ final List tests = new ArrayList<>(T);
+ for (int tc = 0; tc < T; tc++) {
+ final int n = in.nextInt();
+ assert ((1 <= n) && (n <= 10000));
+ final int[] w = new int[n + 1];
+ for (int i = 1; i <= n; i++) w[i] = in.nextInt();
+ final int[][] edges = new int[n - 1][2];
+ for (int i = 0; i < n - 1; i++) {
+ final int a = in.nextInt();
+ final int b = in.nextInt();
+ edges[i][0] = a;
+ edges[i][1] = b;
+ }
+ tests.add(new TestCase(n, w, edges));
+ }
+ return tests;
+ }
+
+ // cal: compute winner using depth-xor heuristic
+ public static List cal(final List inputs) {
+ final List out = new ArrayList<>();
+ for (final var tc : inputs) {
+ final int n = tc.n;
+ final int[] w = tc.w;
+ final List> g = new ArrayList<>(n + 1);
+ g.add(new ArrayList<>()); // dummy for 0-index
+ for (int i = 1; i <= n; i++) g.add(new ArrayList<>());
+ for (final var e : tc.edges) {
+ final int u = e[0], v = e[1];
+ g.get(u).add(v);
+ g.get(v).add(u);
+ }
+ // BFS from root 1 to compute depths (1-based)
+ final int[] depth = new int[n + 1];
+ for (int i = 1; i <= n; i++) depth[i] = -1;
+ final Deque dq = new ArrayDeque<>();
+ depth[1] = 1;
+ dq.addLast(1);
+ while (!dq.isEmpty()) {
+ final int u = dq.removeFirst();
+ for (final var v : g.get(u)) {
+ if (depth[v] == -1) {
+ depth[v] = depth[u] + 1;
+ dq.addLast(v);
+ }
+ }
+ }
+ int x = 0;
+ for (int i = 1; i <= n; i++) {
+ if (w[i] == 1) x ^= depth[i];
+ }
+ out.add(x != 0 ? "YES" : "NO");
+ }
+ return out;
+ }
+
+ // output
+ public static void output(final List