u
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
package main
|
||||
|
||||
// input-scan-cheatsheet.go
|
||||
// ACM/笔试 输入解析速查表(Go)。所有模板均经本机 go1.26.3 实测通过。
|
||||
// 来源:掘金 ACM 模板(starine, 2022) + CSDN fmt/bufio 对比 + 本机实测。
|
||||
//
|
||||
// 本文件与 main.go 同属 package main,但所有模板均为独立函数,
|
||||
// 不与 main.go 的 main() 冲突。直接在 main.go 里调用任一 demoX() 即可验证。
|
||||
//
|
||||
// ───────────────────────── 场景索引 ─────────────────────────
|
||||
// demo1 EOF 多组,每组两个数求和 fmt.Scan
|
||||
// demo2 先读组数 t,再 t 组每组两个数 fmt.Scan
|
||||
// demo3 特殊值结束(0 0 停) fmt.Scan
|
||||
// demo4 先读 n 再读 n 个元素(行布局无关) fmt.Scan ★最省心
|
||||
// demo5 逐行读 + 行内不定个数求和 bufio.Scanner
|
||||
// demo6 先 n 再 n 个元素(Scanner 鲁棒版) bufio.Scanner
|
||||
// demo7 t 组,每组先 n 再 n 个数 bufio.Scanner
|
||||
// demo8 大数据量:Scanner 读 + Writer 输出 bufio 双向缓冲
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ──────────────────────── fmt.Scan 系列 ────────────────────────
|
||||
|
||||
// demo1: 多组数据,每组一行两个整数,读到 EOF 结束。
|
||||
// 输入示例: "1 2\n3 4\n5 7\n" → 3 7 12
|
||||
// 关键:fmt.Scan 把所有空白符(空格/换行/tab)当分隔符,无视行边界。
|
||||
// n 是成功填入的变量个数;n==0 表示 EOF。
|
||||
func demo1() {
|
||||
var a, b int
|
||||
for {
|
||||
n, _ := fmt.Scan(&a, &b)
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
fmt.Println(a + b)
|
||||
}
|
||||
}
|
||||
|
||||
// demo2: 第一行组数 t,接下来 t 行每行两个整数。
|
||||
// 输入示例: "2\n1 2\n3 4\n" → 3 7
|
||||
func demo2() {
|
||||
var t, a, b int
|
||||
fmt.Scan(&t)
|
||||
for i := 0; i < t; i++ {
|
||||
fmt.Scan(&a, &b)
|
||||
fmt.Println(a + b)
|
||||
}
|
||||
}
|
||||
|
||||
// demo3: 多组数据,读到 0 0 结束(特殊哨兵)。
|
||||
// 输入示例: "1 2\n3 4\n0 0\n" → 3 7
|
||||
// 关键:判停条件由题目给定,与 EOF 无关。仍用 fmt.Scan 读取。
|
||||
func demo3() {
|
||||
var a, b int
|
||||
for {
|
||||
fmt.Scan(&a, &b)
|
||||
if a == 0 && b == 0 {
|
||||
break
|
||||
}
|
||||
fmt.Println(a + b)
|
||||
}
|
||||
}
|
||||
|
||||
// demo4 ★最省心模板:先读 n,再读 n 个元素。行布局无关(挤一行/每元素一行/乱排都行)。
|
||||
// 输入1 "5 1 2 3 4 5" 输入2 "5\n1 2 3 4 5" 输入3 "5\n1\n2\n3\n4\n5" 全部 → [1 2 3 4 5]
|
||||
// 关键:fmt.Scan 无视行边界,只按空白切分。naive Scanner 写法在"n 与元素挤一行"时会崩,见 demo6 对比。
|
||||
func demo4() {
|
||||
var n int
|
||||
fmt.Scan(&n)
|
||||
a := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Scan(&a[i])
|
||||
}
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// ──────────────────────── bufio.Scanner 系列 ────────────────────────
|
||||
|
||||
// newScanner 返回带 1MB 缓冲的 Scanner,避免单行过长触发默认 64KB 上限。
|
||||
func newScanner() *bufio.Scanner {
|
||||
sc := bufio.NewScanner(os.Stdin)
|
||||
sc.Buffer(make([]byte, 1024*1024), 1024*1024)
|
||||
return sc
|
||||
}
|
||||
|
||||
// demo5: 多组数据,每行不定个数整数求和。逐行读 + strings.Fields 切分。
|
||||
// 输入示例: "1 2 3\n4 5\n6 7 8 9\n" → 6 9 30
|
||||
// 关键:strings.Fields 按任意空白符切,自动跳过连续空白,不会产生空元素。
|
||||
// 优先用 Fields 而非 strings.Split(s, " ")——后者遇连续空格会产生空字符串。
|
||||
func demo5() {
|
||||
sc := newScanner()
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
var sum int
|
||||
for _, s := range strings.Fields(line) {
|
||||
v, _ := strconv.Atoi(s)
|
||||
sum += v
|
||||
}
|
||||
fmt.Println(sum)
|
||||
}
|
||||
}
|
||||
|
||||
// demo6: 先读 n 再读 n 个元素(Scanner 鲁棒版)。兼容"n 与元素挤一行"的情况。
|
||||
// 关键:naive 写法(sc.Scan 一行当 n 去解析)在 "5 1 2 3 4 5" 上会 Atoi 失败返回 0。
|
||||
// 鲁棒做法:把第一行用 Fields 切开,第一个 token 是 n,余下的直接进数组,不够再读后续行。
|
||||
func demo6() {
|
||||
sc := newScanner()
|
||||
sc.Scan()
|
||||
tokens := strings.Fields(sc.Text())
|
||||
n, _ := strconv.Atoi(tokens[0])
|
||||
a := make([]int, 0, n)
|
||||
for _, s := range tokens[1:] {
|
||||
v, _ := strconv.Atoi(s)
|
||||
a = append(a, v)
|
||||
}
|
||||
for len(a) < n && sc.Scan() {
|
||||
for _, s := range strings.Fields(sc.Text()) {
|
||||
v, _ := strconv.Atoi(s)
|
||||
a = append(a, v)
|
||||
}
|
||||
}
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// demo7: t 组,每组先读 n 再读 n 个整数求和。
|
||||
// 输入示例: "2\n3 1 2 3\n2 4 5\n" → 6 9
|
||||
// 组合模式:外层组数用 Scan,内层元素用 Scanner 按行 + Fields 收集。
|
||||
func demo7() {
|
||||
sc := newScanner()
|
||||
sc.Scan()
|
||||
t, _ := strconv.Atoi(strings.TrimSpace(sc.Text()))
|
||||
for i := 0; i < t; i++ {
|
||||
sc.Scan()
|
||||
tokens := strings.Fields(sc.Text())
|
||||
n, _ := strconv.Atoi(tokens[0])
|
||||
a := make([]int, 0, n)
|
||||
for _, s := range tokens[1:] {
|
||||
v, _ := strconv.Atoi(s)
|
||||
a = append(a, v)
|
||||
}
|
||||
for len(a) < n && sc.Scan() {
|
||||
for _, s := range strings.Fields(sc.Text()) {
|
||||
v, _ := strconv.Atoi(s)
|
||||
a = append(a, v)
|
||||
}
|
||||
}
|
||||
sum := 0
|
||||
for _, v := range a {
|
||||
sum += v
|
||||
}
|
||||
fmt.Println(sum)
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────── 大数据量模板 ────────────────────────
|
||||
|
||||
// demo8: 大数据量(>10^5 行)。Scanner 读 + Writer 输出,双向缓冲。
|
||||
// 输入示例: 大量行,每行一个整数,输出每个的 2 倍。
|
||||
// 关键:fmt.Print 系列在 10^5 级输出时会明显变慢;bufio.NewWriter + Flush 提速一个量级。
|
||||
// defer w.Flush() 必须有,否则缓冲区内容不会写出。
|
||||
func demo8() {
|
||||
sc := newScanner()
|
||||
w := bufio.NewWriter(os.Stdout)
|
||||
defer w.Flush()
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
v, _ := strconv.Atoi(strings.TrimSpace(line))
|
||||
fmt.Fprintln(w, v*2)
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────── 坑速查 ────────────────────────
|
||||
// 1. fmt.Scan 读不全时,未填的变量保留上轮值(不会清零)。
|
||||
// → 判停用 n < 预期个数,别信任未读到的变量。
|
||||
// 2. naive Scanner(整行 Atoi 当 n)在"n 与元素挤一行"时返回 n=0。
|
||||
// → 用 strings.Fields 切开第一行(见 demo6)。
|
||||
// 3. strings.Split(s, " ") 遇连续空格产生空字符串元素。
|
||||
// → 优先 strings.Fields(s)。
|
||||
// 4. bufio.Scanner 默认 64KB 上限,超长行会读不全。
|
||||
// → sc.Buffer(make([]byte, 1024*1024), 1024*1024)。
|
||||
// 5. bufio.NewWriter 必须 Flush,否则输出丢失。
|
||||
// → defer w.Flush()。
|
||||
// 6. fmt.Scanln 遇换行即停,不跨行;fmt.Scan 无视行边界跨行连读。
|
||||
// → ACM 默认用 fmt.Scan。
|
||||
@@ -0,0 +1,109 @@
|
||||
package misc
|
||||
|
||||
import "testing"
|
||||
|
||||
// 题目(推测题干)
|
||||
//
|
||||
// 给定两个长度为 n 的 0/1 数组 a 和 b。
|
||||
// 一次操作:选择 [L, R, V],满足 1 ≤ L ≤ R ≤ n,V ∈ {0,1},
|
||||
// 将 a 中下标在 [L,R] 内的所有元素统一修改为 V(区间外不变)。
|
||||
//
|
||||
// 问:是否存在恰好一种操作使 a 变为 b?
|
||||
// - 存在且唯一:输出该操作 [L,R,V](0-indexed 闭区间)
|
||||
// - 不存在或不唯一:输出 -1
|
||||
//
|
||||
// 思路(0-indexed)
|
||||
//
|
||||
// 记 need = { i : a[i] != b[i] },即必须被改到的位置。
|
||||
//
|
||||
// 1. 找 lo = min(need), hi = max(need)。need 为空 → a == b,特判(见下)。
|
||||
// 2. 区间 [L,R] 必须 ⊇ [lo, hi],否则 need 中落在区间外的位置不会被改。
|
||||
// 3. 区间内每个位置改完都要等于 b;而一个操作只有一个 V,
|
||||
// 所以 [lo,hi] 内 b 必须全相同,V = b[lo]。否则无解 (-1)。
|
||||
// 4. L 的取值范围:L ≤ lo。向左能扩张到 b 中连续等于 V 的位置
|
||||
// (这些位置改后仍等于 b,不破坏)。
|
||||
// - L 唯一 ⟺ lo == 0(到左边界)或 b[lo-1] != V(左边紧邻不等于 V,无法扩张)
|
||||
// 5. R 的取值范围:R ≥ hi。向右能扩张到 b 中连续等于 V 的位置。
|
||||
// - R 唯一 ⟺ hi == n-1(到右边界)或 b[hi+1] != V
|
||||
// 6. L、R 都唯一 → 输出 [lo, hi, V];否则 -1。
|
||||
//
|
||||
// 特判 a == b(need 为空):
|
||||
// - 要一次操作让 a 不变:区间内 a 必须全等于 V。
|
||||
// - 任何单点 [i,i] 配 V=a[i] 都可行。
|
||||
// - n == 1:唯一解 [0, 0, a[0]]。
|
||||
// - n >= 2:有多个单点选择 → 不唯一 → -1。
|
||||
|
||||
func uniqueRangeAssign(a, b []int) (L, R, V int, ok bool) {
|
||||
n := len(a)
|
||||
|
||||
// 第 1 步:找 lo, hi
|
||||
lo, hi := -1, -1
|
||||
for i := 0; i < n; i++ {
|
||||
if a[i] != b[i] {
|
||||
if lo == -1 {
|
||||
lo = i
|
||||
}
|
||||
hi = i
|
||||
}
|
||||
}
|
||||
|
||||
// 特判 a == b
|
||||
if lo == -1 {
|
||||
if n == 1 {
|
||||
return 0, 0, a[0], true
|
||||
}
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
|
||||
// 第 3 步:V = b[lo],检查 [lo,hi] 内 b 全等于 V
|
||||
V = b[lo]
|
||||
for i := lo; i <= hi; i++ {
|
||||
if b[i] != V {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// 第 4 步:L 唯一性
|
||||
lOK := lo == 0 || b[lo-1] != V
|
||||
// 第 5 步:R 唯一性
|
||||
rOK := hi == n-1 || b[hi+1] != V
|
||||
|
||||
if !lOK || !rOK {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
|
||||
// 第 6 步:唯一解
|
||||
return lo, hi, V, true
|
||||
}
|
||||
|
||||
func TestUniqueRangeAssign(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
a, b []int
|
||||
wantL int
|
||||
wantR int
|
||||
wantV int
|
||||
wantOK bool
|
||||
}{
|
||||
{"唯一-全改", []int{0, 0}, []int{1, 1}, 0, 1, 1, true},
|
||||
{"唯一-部分改", []int{0, 0, 0, 0, 0}, []int{1, 1, 1, 0, 0}, 0, 2, 1, true},
|
||||
{"唯一-右扩", []int{0, 0, 0, 0, 0}, []int{0, 0, 1, 1, 1}, 2, 4, 1, true},
|
||||
{"无解-内部b不一致", []int{0, 1, 0}, []int{1, 0, 1}, 0, 0, 0, false},
|
||||
{"不唯一-R可扩", []int{0, 1, 1}, []int{1, 1, 1}, 0, 0, 0, false},
|
||||
{"不唯一-L可扩", []int{1, 0, 1}, []int{1, 1, 1}, 0, 0, 0, false},
|
||||
{"不唯一-a=b,n>=2", []int{1, 1, 1}, []int{1, 1, 1}, 0, 0, 0, false},
|
||||
{"唯一-a=b,n=1", []int{0}, []int{0}, 0, 0, 0, true},
|
||||
{"唯一-单点改", []int{0}, []int{1}, 0, 0, 1, true},
|
||||
{"唯一-覆盖整个数组", []int{1, 1, 0, 1}, []int{0, 0, 0, 0}, 0, 3, 0, true},
|
||||
{"无解-need不连续且b不一致", []int{0, 0, 0, 0}, []int{1, 0, 0, 1}, 0, 0, 0, false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
L, R, V, ok := uniqueRangeAssign(c.a, c.b)
|
||||
pass := ok == c.wantOK &&
|
||||
(!ok || (L == c.wantL && R == c.wantR && V == c.wantV))
|
||||
if !pass {
|
||||
t.Errorf("%s: a=%v b=%v -> [%d,%d,%d] ok=%v, want [%d,%d,%d] ok=%v",
|
||||
c.name, c.a, c.b, L, R, V, ok, c.wantL, c.wantR, c.wantV, c.wantOK)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user