package main

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

var sc = bufio.NewScanner(os.Stdin)

func Scan() string {
	sc.Scan()
	return sc.Text()
}
func rScan() []rune {
	return []rune(Scan())
}
func iScan() int {
	n, _ := strconv.Atoi(Scan())
	return n
}
func fScan() float64 {
	n, _ := strconv.ParseFloat(Scan(), 64)
	return n
}
func stringToInt(s string) int {
	n, _ := strconv.Atoi(s)
	return n
}
func SScan(n int) []string {
	a := make([]string, n)
	for i := 0; i < n; i++ {
		a[i] = Scan()
	}
	return a
}
func iSScan(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = iScan()
	}
	return a
}
func abs(x int) int {
	if x < 0 {
		x = -x
	}
	return x
}
func mod(x, d int) int {
	x %= d
	if x < 0 {
		x += d
	}
	return x
}
func larger(a, b int) int {
	if a < b {
		return b
	} else {
		return a
	}
}
func smaller(a, b int) int {
	if a > b {
		return b
	} else {
		return a
	}
}
func max(a []int) int {
	x := a[0]
	for i := 0; i < len(a); i++ {
		if x < a[i] {
			x = a[i]
		}
	}
	return x
}
func min(a []int) int {
	x := a[0]
	for i := 0; i < len(a); i++ {
		if x > a[i] {
			x = a[i]
		}
	}
	return x
}
func sum(a []int) int {
	x := 0
	for _, v := range a {
		x += v
	}
	return x
}
func fSum(a []float64) float64 {
	x := 0.
	for _, v := range a {
		x += v
	}
	return x
}
func bPrint(f bool, x string, y string) {
	if f {
		fmt.Println(x)
	} else {
		fmt.Println(y)
	}
}
func iSSPrint(x []int) {
	fmt.Println(strings.Trim(fmt.Sprint(x), "[]"))
}

var lp int = 1000000007

func main() {
	buf := make([]byte, 0)
	sc.Buffer(buf, lp)
	sc.Split(bufio.ScanWords)
	n, m := iScan(), iScan()
	grid := SScan(n)
	g := newMFG(n*m + 2)
	s := n * m
	t := n*m + 1
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if string(grid[i][j]) != "#" {
				v := i*m + j
				if (i+j)%2 == 0 {
					g.AddEdge(s, v, 1)
				} else {
					g.AddEdge(v, t, 1)
				}
			}
		}
	}
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if string(grid[i][j]) == "#" || (i+j)%2 == 1 {
				continue
			}
			v := i*m + j
			if i > 0 {
				if string(grid[i-1][j]) == "." {
					g.AddEdge(v, (i-1)*m+j, 1)
				}
			}
			if j > 0 {
				if string(grid[i][j-1]) == "." {
					g.AddEdge(v, i*m+j-1, 1)
				}
			}
			if i < n-1 {
				if string(grid[i+1][j]) == "." {
					g.AddEdge(v, (i+1)*m+j, 1)
				}
			}
			if j < m-1 {
				if string(grid[i][j+1]) == "." {
					g.AddEdge(v, i*m+j+1, 1)
				}
			}
		}
	}
	fmt.Println(g.Flow(s, t))
	edg := g.EdgesList()
	ans := make([][]string, n)
	for i := 0; i < n; i++ {
		ans[i] = make([]string, m)
		for j := 0; j < m; j++ {
			ans[i][j] = string(grid[i][j])
		}
	}
	for _, e := range edg {
		if e.from == s || e.to == t || e.flow == 0 {
			continue
		}
		fi, fj := e.from/m, e.from%m
		ti, tj := e.to/m, e.to%m
		if fi == ti {
			ans[fi][smaller(fj, tj)] = ">"
			ans[fi][larger(fj, tj)] = "<"
		} else {
			ans[smaller(fi, ti)][fj] = "v"
			ans[larger(fi, ti)][fj] = "^"
		}
	}
	for _, v := range ans {
		fmt.Println(strings.Join(v, ""))
	}
}

type Edge struct {
	from int
	to   int
	capa int
	flow int
}
type _Edge struct {
	to   int
	rev  int
	capa int
}
type MaxFlowGraph struct {
	n   int
	pos [][2]int
	g   [][]_Edge
}

func newMFG(n int) *MaxFlowGraph {
	return &MaxFlowGraph{
		n: n,
		g: make([][]_Edge, n),
	}
}
func (graph *MaxFlowGraph) smaller(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func (graph *MaxFlowGraph) AddEdge(from, to, capa int) int {
	m := len(graph.pos)
	graph.pos = append(graph.pos, [2]int{from, len(graph.g[from])})
	graph.g[from] = append(graph.g[from], _Edge{to, len(graph.g[to]), capa})
	graph.g[to] = append(graph.g[to], _Edge{from, len(graph.g[from]) - 1, 0})
	return m
}
func (graph *MaxFlowGraph) GetEdge(i int) Edge {
	_e := graph.g[graph.pos[i][0]][graph.pos[i][1]]
	_re := graph.g[_e.to][_e.rev]
	return Edge{graph.pos[i][0], _e.to, _e.capa + _re.capa, _re.capa}
}
func (graph *MaxFlowGraph) EdgesList() []Edge {
	m := len(graph.pos)
	result := make([]Edge, 0, m)
	for i := 0; i < m; i++ {
		result = append(result, graph.GetEdge(i))
	}
	return result
}
func (graph *MaxFlowGraph) ChangeEdge(i, newCapa, newFlow int) {
	_e := &graph.g[graph.pos[i][0]][graph.pos[i][1]]
	_re := &graph.g[_e.to][_e.rev]
	_e.capa = newCapa - newFlow
	_re.capa = newFlow
}
func (graph *MaxFlowGraph) Flow(s, t int) int {
	return graph.FlowL(s, t, int(1e+18))
}
func (graph *MaxFlowGraph) FlowL(s, t, flowLim int) int {
	level := make([]int, graph.n)
	iter := make([]int, graph.n)
	bfs := func() {
		for i, _ := range level {
			level[i] = -1
		}
		level[s] = 0
		q := make([]int, 0, graph.n)
		q = append(q, s)
		for len(q) != 0 {
			v := q[0]
			q = q[1:]
			for _, e := range graph.g[v] {
				if e.capa == 0 || level[e.to] >= 0 {
					continue
				}
				level[e.to] = level[v] + 1
				if e.to == t {
					return
				}
				q = append(q, e.to)
			}
		}
	}
	var dfs func(v, up int) int
	dfs = func(v, up int) int {
		if v == s {
			return up
		}
		res := 0
		lv := level[v]
		for ; iter[v] < len(graph.g[v]); iter[v]++ {
			e := &graph.g[v][iter[v]]
			if lv <= level[e.to] || graph.g[e.to][e.rev].capa == 0 {
				continue
			}
			d := dfs(e.to, graph.smaller(up-res, graph.g[e.to][e.rev].capa))
			if d <= 0 {
				continue
			}
			graph.g[v][iter[v]].capa += d
			graph.g[e.to][e.rev].capa -= d
			res += d
			if res == up {
				break
			}
		}
		return res
	}
	flow := 0
	for flow < flowLim {
		bfs()
		if level[t] == -1 {
			break
		}
		for i, _ := range iter {
			iter[i] = 0
		}
		for flow < flowLim {
			f := dfs(t, flowLim-flow)
			if f == 0 {
				break
			}
			flow += f
		}
	}
	return flow
}
func (graph *MaxFlowGraph) MinCut(s int) []bool {
	visited := make([]bool, graph.n)
	q := make([]int, 0, graph.n)
	q = append(q, s)
	for len(q) != 0 {
		p := q[0]
		q = q[1:]
		visited[p] = true
		for _, e := range graph.g[p] {
			if e.capa > 0 && !visited[e.to] {
				visited[e.to] = true
				q = append(q, e.to)
			}
		}
	}
	return visited
}


Measured memory_bytes: 9212