package main

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

type Area interface {
	content() [][]int
}

type Pattern [][]int

func (p Pattern) content() [][]int {
	return p
}

type Board [][]int

func (b Board) content() [][]int {
	return b
}

func equal(xs []int, ys []int) bool {
	if xs == nil {
		return ys == nil
	} else if ys == nil {
		return xs == nil
	}
	if len(xs) != len(ys) {
		return false
	}
	for i := 0; i < len(xs); i++ {
		if xs[i] != ys[i] {
			return false
		}
	}
	return true
}

func Rows(area Area) [][]int {
	return area.content()
}

func Cols(area Area) [][]int {
	p := area.content()
	if len(p) == 0 {
		panic(fmt.Errorf("pattern should have more than one rows"))
	}
	res := make([][]int, len(p[0]))
	for i := 0; i < len(res); i++ {
		res[i] = make([]int, len(p))
		for j := 0; j < len(res[i]); j++ {
			res[i][j] = p[j][i]
		}
	}
	return res
}

func computeRowFunction(pattern Pattern) func(int)int {
	return computeFunction(Rows(pattern))
}

func computeColFunction(pattern Pattern) func(int)int {
	return computeFunction(Cols(pattern))
}

func computeFunction(is [][]int) func(int)int {
	res := make([]int, len(is))
	k := 0

	for i := 1; i < len(is); i++ {
		for k > 0 && !equal(is[k], is[i]) {
			k = res[k-1]
		}
		if equal(is[k], is[i]) {
			k += 1
			res[i] = k
		}
	}

	return func(index int) int {
		return res[index]
	}
}

type Position struct {
	r int
	c int
}

func (p Position) String() string {
	return strconv.Itoa(p.r) + " " + strconv.Itoa(p.c)
}

func doMatch(pattern Pattern, rowF func(int)int, colF func(int)int, board Board) []Position {
	bRows := Rows(board)
	bCols := Cols(board)
	pRows := Rows(pattern)
	pCols := Cols(pattern)
	res := []Position{}

	/* TODO: is it possible (and faster) to apply KMP for rows as well as cols? */
	for i := 0; i < len(bRows)-len(pRows)+1; i++ {
		k := 0
		for j := 0; j < len(bCols); j++ {
			for k > 0 && !equal(pCols[k], bCols[j][i:i+len(pRows)]) {
				k = colF(k-1)
			}
			if equal(pCols[k], bCols[j][i:i+len(pRows)]) {
				k += 1
				if k == len(pCols) {
					res = append(res, Position{i, j - k + 1})
					k = colF(k-1)
				}
			}
		}
	}

	return res
}

func Match(pattern Pattern, board Board) []Position {
	rowF := computeRowFunction(pattern)
	colF := computeColFunction(pattern)
	return doMatch(pattern, rowF, colF, board)
}

func main() {
	var r, c int

	sc := bufio.NewScanner(os.Stdin)
	sc.Split(bufio.ScanWords)

	r, err := nextInt(sc)
	if err != nil {
		panic(err)
	}
	c, err = nextInt(sc)
	if err != nil {
		panic(err)
	}

	board := make(Board, r)
	for i := 0; i < r; i++ {
		board[i] = make([]int, c)
		for j, ch := range nextText(sc) {
			board[i][j] = int(ch)
		}
	}
	
	r, err = nextInt(sc)
	if err != nil {
		panic(err)
	}
	c, err = nextInt(sc)
	if err != nil {
		panic(err)
	}

	pattern := make(Pattern, r)
	for i := 0; i < r; i++ {
		pattern[i] = make([]int, c)
		for j, ch := range nextText(sc) {
			pattern[i][j] = int(ch)
		}
	}

	positions := Match(pattern, board)
	if len(positions) > 0 {
		strs := make([]string, len(positions))
		for i := 0; i < len(strs); i++ {
			strs[i] = positions[i].String()
		}
		fmt.Println(strings.Join(strs, "\n"))
	}
}

func nextInt(sc *bufio.Scanner) (int, error) {
	return strconv.Atoi(nextText(sc))
}

func nextText(sc *bufio.Scanner) string {
	sc.Scan()
	return sc.Text()
}


Measured memory_bytes: 52376