← Home
```go
package main

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

// Node represents a node in the Trie.
// Uses a child-sibling representation to save memory.
type Node struct {
	char        byte
	depth       int
	firstChild  *Node
	nextSibling *Node
	students    []int
	pseudos     []int
}

var (
	matchResult []int
	totalScore  int64
)

func main() {
	// Setup fast I/O
	scanner := bufio.NewScanner(os.Stdin)
	// Input buffer to handle large inputs, though tokens are split by words
	buf := make([]byte, 4096*1024)
	scanner.Buffer(buf, len(buf))
	scanner.Split(bufio.ScanWords)

	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	scan := func() string {
		if !scanner.Scan() {
			return ""
		}
		return scanner.Text()
	}

	token := scan()
	if token == "" {
		return
	}
	n, _ := strconv.Atoi(token)

	root := &Node{depth: 0}

	// Read Student Names
	for i := 1; i <= n; i++ {
		s := scan()
		addString(root, s, i, true)
	}

	// Read Pseudonyms
	for i := 1; i <= n; i++ {
		s := scan()
		addString(root, s, i, false)
	}

	matchResult = make([]int, n+1)
	dfs(root)

	// Output results
	fmt.Fprintln(writer, totalScore)
	for i := 1; i <= n; i++ {
		fmt.Fprintf(writer, "%d %d\n", i, matchResult[i])
	}
}

// addString inserts a string into the Trie
func addString(u *Node, s string, id int, isStudent bool) {
	node := u
	for i := 0; i < len(s); i++ {
		c := s[i]
		// Find child with character c
		var child *Node
		curr := node.firstChild
		for curr != nil {
			if curr.char == c {
				child = curr
				break
			}
			curr = curr.nextSibling
		}

		// If not found, create new child
		if child == nil {
			child = &Node{char: c, depth: node.depth + 1, nextSibling: node.firstChild}
			node.firstChild = child
		}
		node = child
	}

	if isStudent {
		node.students = append(node.students, id)
	} else {
		node.pseudos = append(node.pseudos, id)
	}
}

// dfs traverses the Trie post-order to perform greedy matching
func dfs(u *Node) {
	child := u.firstChild
	for child != nil {
		dfs(child)
		// Collect unmatched indices from children (move to current node)
		if len(child.students) > 0 {
			u.students = append(u.students, child.students...)
		}
		if len(child.pseudos) > 0 {
			u.pseudos = append(u.pseudos, child.pseudos...)
		}
		child = child.nextSibling
	}

	// Greedy matching at the current node
	sCount := len(u.students)
	pCount := len(u.pseudos)
	matches := sCount
	if pCount < matches {
		matches = pCount
	}

	if matches > 0 {
		for i := 0; i < matches; i++ {
			// Pair the last elements
			sID := u.students[sCount-1-i]
			pID := u.pseudos[pCount-1-i]
			matchResult[sID] = pID
		}
		// Add score: matches * current depth (LCP length)
		totalScore += int64(matches) * int64(u.depth)

		// Remove matched elements from the lists
		u.students = u.students[:sCount-matches]
		u.pseudos = u.pseudos[:pCount-matches]
	}
}
```