```go
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
buf, _ := io.ReadAll(os.Stdin)
pos := 0
nextWord := func() string {
for pos < len(buf) && buf[pos] <= ' ' {
pos++
}
if pos >= len(buf) {
return ""
}
start := pos
for pos < len(buf) && buf[pos] > ' ' {
pos++
}
return string(buf[start:pos])
}
nextInt := func() int {
for pos < len(buf) && buf[pos] <= ' ' {
pos++
}
if pos >= len(buf) {
return 0
}
res := 0
for pos < len(buf) && buf[pos] > ' ' {
res = res*10 + int(buf[pos]-'0')
pos++
}
return res
}
n := nextInt()
m := nextInt()
if n == 0 || m == 0 {
return
}
max_states := 300005
children := make([][26]int32, max_states)
victims_at := make([][]int, max_states)
sz := 1
for i := 1; i <= n; i++ {
s := nextWord()
curr := 0
for j := 0; j < len(s); j++ {
c := s[j] - 'a'
if children[curr][c] == 0 {
children[curr][c] = int32(sz)
sz++
}
curr = int(children[curr][c])
}
victims_at[curr] = append(victims_at[curr], i)
}
fail := make([]int, sz)
queue := make([]int, 0, sz)
for i := 0; i < 26; i++ {
if children[0][i] != 0 {
fail[children[0][i]] = 0
queue = append(queue, int(children[0][i]))
}
}
for len(queue) > 0 {
u := queue[0]
queue = queue[1:]
for i := 0; i < 26; i++ {
v := int(children[u][i])
if v != 0 {
fail[v] = int(children[fail[u]][i])
queue = append(queue, v)
} else {
children[u][i] = children[fail[u]][i]
}
}
}
fail_tree := make([][]int, sz)
for i := 1; i < sz; i++ {
fail_tree[fail[i]] = append(fail_tree[fail[i]], i)
}
size := make([]int, sz)
heavy := make([]int, sz)
for i := range heavy {
heavy[i] = -1
}
head := make([]int, sz)
dfn := make([]int, sz)
dfn_to_node := make([]int, sz)
timer := 0
var dfs1 func(u int)
dfs1 = func(u int) {
size[u] = 1
max_sub := 0
for _, v := range fail_tree[u] {
dfs1(v)
size[u] += size[v]
if size[v] > max_sub {
max_sub = size[v]
heavy[u] = v
}
}
}
var dfs2 func(u, h int)
dfs2 = func(u, h int) {
head[u] = h
dfn[u] = timer
dfn_to_node[timer] = u
timer++
if heavy[u] != -1 {
dfs2(heavy[u], h)
}
for _, v := range fail_tree[u] {
if v != heavy[u] {
dfs2(v, v)
}
}
}
dfs1(0)
dfs2(0, 0)
start_vic := make([]int, sz)
end_vic := make([]int, sz)
pos_in_segtree := make([]int, n+1)
seg_timer := 0
for i := 0; i < sz; i++ {
u := dfn_to_node[i]
start_vic[u] = seg_timer
for _, vic_id := range victims_at[u] {
pos_in_segtree[vic_id] = seg_timer
seg_timer++
}
end_vic[u] = seg_timer - 1
}
tree := make([]int, 4*n+4)
var update_seg func(node, start, end, idx, val int)
update_seg = func(node, start, end, idx, val int) {
if start == end {
tree[node] = val
return
}
mid := (start + end) / 2
if idx <= mid {
update_seg(2*node, start, mid, idx, val)
} else {
update_seg(2*node+1, mid+1, end, idx, val)
}
tree[node] = max(tree[2*node], tree[2*node+1])
}
var query_seg func(node, start, end, l, r int) int
query_seg = func(node, start, end, l, r int) int {
if r < start || end < l {
return -1
}
if l <= start && end <= r {
return tree[node]
}
mid := (start + end) / 2
res := -1
if l <= mid {
res = max(res, query_seg(2*node, start, mid, l, r))
}
if r > mid {
res = max(res, query_seg(2*node+1, mid+1, end, l, r))
}
return res
}
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
last_query := make([]int, sz)
query_id := 0
for i := 0; i < m; i++ {
type_q := nextInt()
if type_q == 1 {
idx := nextInt()
x := nextInt()
update_seg(1, 0, n-1, pos_in_segtree[idx], x)
} else {
q := nextWord()
query_id++
ans := -1
curr := 0
for j := 0; j < len(q); j++ {
curr = int(children[curr][q[j]-'a'])
u := curr
for u != 0 && last_query[u] != query_id {
last_query[u] = query_id
top := head[u]
l := start_vic[top]
r := end_vic[u]
if l <= r {
ans = max(ans, query_seg(1, 0, n-1, l, r))
}
u = fail[top]
}
}
fmt.Fprintln(out, ans)
}
}
}
```