← Home
```go
package main

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

// SAM Node
type Node struct {
	len  int
	link int
	next map[rune]int
}

var st []Node
var sz, last int

func samInit(n int) {
	st = make([]Node, n*2)
	st[0].len = 0
	st[0].link = -1
	st[0].next = make(map[rune]int)
	sz = 1
	last = 0
}

func samExtend(c rune) int {
	cur := sz
	sz++
	st[cur].len = st[last].len + 1
	st[cur].next = make(map[rune]int)
	// st[cur].link is 0 by default
	p := last
	for p != -1 {
		if _, ok := st[p].next[c]; ok {
			break
		}
		st[p].next[c] = cur
		p = st[p].link
	}
	if p == -1 {
		st[cur].link = 0
	} else {
		q := st[p].next[c]
		if st[p].len+1 == st[q].len {
			st[cur].link = q
		} else {
			clone := sz
			sz++
			st[clone].len = st[p].len + 1
			st[clone].next = make(map[rune]int)
			for k, v := range st[q].next {
				st[clone].next[k] = v
			}
			st[clone].link = st[q].link
			for p != -1 {
				if st[p].next[c] != q {
					break
				}
				st[p].next[c] = clone
				p = st[p].link
			}
			st[q].link = clone
			st[cur].link = clone
		}
	}
	last = cur
	return cur
}

// Segment Tree for merging positions
type SegNode struct {
	l, r *SegNode
}

func update(node *SegNode, l, r, idx int) *SegNode {
	if node == nil {
		node = &SegNode{}
	}
	if l == r {
		return node
	}
	mid := (l + r) / 2
	if idx <= mid {
		node.l = update(node.l, l, mid, idx)
	} else {
		node.r = update(node.r, mid+1, r, idx)
	}
	return node
}

func merge(a, b *SegNode) *SegNode {
	if a == nil {
		return b
	}
	if b == nil {
		return a
	}
	a.l = merge(a.l, b.l)
	a.r = merge(a.r, b.r)
	return a
}

func collect(node *SegNode, l, r int, res *[]int) {
	if node == nil {
		return
	}
	if l == r {
		*res = append(*res, l)
		return
	}
	mid := (l + r) / 2
	collect(node.l, l, mid, res)
	collect(node.r, mid+1, r, res)
}

type Query struct {
	k, len, id int
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	line, _ := reader.ReadString('\n')
	s := strings.TrimSpace(line)
	n := len(s)

	samInit(n + 1)
	prefixState := make([]int, n)
	for i, char := range s {
		prefixState[i] = samExtend(char)
	}

	var numQueries int
	fmt.Fscan(reader, &numQueries)

	queries := make([][]Query, sz)
	answers := make([]int, numQueries)

	for i := 0; i < numQueries; i++ {
		var k int
		var m string
		fmt.Fscan(reader, &k, &m)

		curr := 0
		possible := true
		for _, c := range m {
			if next, ok := st[curr].next[c]; ok {
				curr = next
			} else {
				possible = false
				break
			}
		}

		if !possible {
			answers[i] = -1
		} else {
			queries[curr] = append(queries[curr], Query{k, len(m), i})
		}
	}

	adj := make([][]int, sz)
	for i := 1; i < sz; i++ {
		link := st[i].link
		if link != -1 {
			adj[link] = append(adj[link], i)
		}
	}

	nodePositions := make([]*SegNode, sz)
	for i, nodeIdx := range prefixState {
		nodePositions[nodeIdx] = update(nodePositions[nodeIdx], 0, n-1, i)
	}

	var dfs func(u int)
	dfs = func(u int) {
		for _, v := range adj[u] {
			dfs(v)
			nodePositions[u] = merge(nodePositions[u], nodePositions[v])
		}

		if len(queries[u]) > 0 {
			var posList []int
			collect(nodePositions[u], 0, n-1, &posList)
			cnt := len(posList)

			for _, q := range queries[u] {
				if cnt < q.k {
					answers[q.id] = -1
				} else {
					minLen := int(2e9)
					// posList contains end positions sorted
					// We need to find min length of substring containing k occurrences
					// Length = posList[j+k-1] - posList[j] + |m|
					for j := 0; j <= cnt-q.k; j++ {
						l := posList[j+q.k-1] - posList[j] + q.len
						if l < minLen {
							minLen = l
						}
					}
					answers[q.id] = minLen
				}
			}
		}
	}

	dfs(0)

	for _, ans := range answers {
		fmt.Fprintln(writer, ans)
	}
}
```