package main

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

var sc, wr = bufio.NewScanner(os.Stdin), bufio.NewWriter(os.Stdout)

func scanString() string   { sc.Scan(); return sc.Text() }
func scanRunes() []rune    { return []rune(scanString()) }
func scanInt() int         { a, _ := strconv.Atoi(scanString()); return a }
func scanInt64() int64     { a, _ := strconv.ParseInt(scanString(), 10, 64); return a }
func scanFloat64() float64 { a, _ := strconv.ParseFloat(scanString(), 64); return a }

func scanInts(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = scanInt()
	}
	return a
}

func debug(a ...interface{}) {
	if os.Getenv("ONLINE_JUDGE") == "false" {
		fmt.Fprintln(os.Stderr, a...)
	}
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

//•*¨*•.¸¸♪main•*¨*•.¸¸♪(　-ω-)ノ　(　・ω・)
func main() {
	defer wr.Flush()
	sc.Split(bufio.ScanWords)
	sc.Buffer(make([]byte, 10000), 1001001)

	n, m := scanInt(), scanInt()
	g := make([][]rune, n)
	for i := 0; i < n; i++ {
		g[i] = scanRunes()
	}

	mf := newMaxFlow(n*m + 2)
	s, t := n*m, n*m+1

	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if g[i][j] == '#' {
				continue
			}

			v := i*m + j
			if (i+j)&1 == 0 {
				mf.addEdge(s, v, 1)
				if i != 0 && g[i-1][j] != '#' {
					mf.addEdge(v, (i-1)*m+j, 1)
				}
				if i != n-1 && g[i+1][j] != '#' {
					mf.addEdge(v, (i+1)*m+j, 1)
				}
				if j != 0 && g[i][j-1] != '#' {
					mf.addEdge(v, i*m+j-1, 1)
				}
				if j != m-1 && g[i][j+1] != '#' {
					mf.addEdge(v, i*m+j+1, 1)
				}
			} else {
				mf.addEdge(v, t, 1)
			}
		}
	}

	fmt.Fprintln(wr, mf.flow(s, t))

	es := mf.edges()
	for _, e := range es {
		if e.from == s || e.to == t || e.flow == 0 {
			continue
		}

		y0, x0 := e.from/m, e.from%m
		y1, x1 := e.to/m, e.to%m
		debug(y0, x0, y1, x1)
		if x0-x1 == 1 {
			g[y0][x0] = '<'
			g[y1][x1] = '>'
		} else if x0-x1 == -1 {
			g[y0][x0] = '>'
			g[y1][x1] = '<'
		} else if y0-y1 == 1 {
			g[y0][x0] = '^'
			g[y1][x1] = 'v'
		} else {
			g[y0][x0] = 'v'
			g[y1][x1] = '^'
		}
	}

	for i := 0; i < n; i++ {
		fmt.Fprintln(wr, string(g[i]))
	}

}

type edge struct{ from, to, cap, flow int }
type _edge struct{ to, cap, rev int }
type maxFlow struct {
	g    [][]_edge
	es   [][2]int
	lv   []int
	iter []int
	inf  int
}

func newMaxFlow(n int) *maxFlow {
	mf := maxFlow{make([][]_edge, n), [][2]int{}, make([]int, n), make([]int, n), 1 << 60}
	return &mf
}

func (mf *maxFlow) addEdge(from, to, cap int) {
	mf.es = append(mf.es, [2]int{from, len(mf.g[from])})
	mf.g[from] = append(mf.g[from], _edge{to, cap, len(mf.g[to])})
	mf.g[to] = append(mf.g[to], _edge{from, 0, len(mf.g[from]) - 1})
}

func (mf *maxFlow) edge(i int) edge {
	v := mf.es[i]
	e := mf.g[v[0]][v[1]]
	r := mf.g[e.to][e.rev]
	return edge{v[0], e.to, e.cap + r.cap, r.cap}
}

func (mf *maxFlow) edges() []edge {
	es := []edge{}
	for i := 0; i < len(mf.es); i++ {
		es = append(es, mf.edge(i))
	}
	return es
}

func (mf *maxFlow) bfs(s int) {
	for i := 0; i < len(mf.g); i++ {
		mf.lv[i] = -1
	}
	mf.lv[s] = 0
	q := []int{s}
	for len(q) != 0 {
		v := q[0]
		q = q[1:]
		for _, e := range mf.g[v] {
			if e.cap == 0 || mf.lv[e.to] >= 0 {
				continue
			}
			mf.lv[e.to] = mf.lv[v] + 1
			q = append(q, e.to)
		}
	}
}

func (mf *maxFlow) dfs(v, t, f int) int {
	if v == t {
		return f
	}

	for i := &mf.iter[v]; *i < len(mf.g[v]); *i++ {
		e := &mf.g[v][*i]
		if e.cap == 0 || mf.lv[v] >= mf.lv[e.to] {
			continue
		}
		mi := f
		if mi > e.cap {
			mi = e.cap
		}
		if d := mf.dfs(e.to, t, mi); d > 0 {
			e.cap -= d
			mf.g[e.to][e.rev].cap += d
			return d
		}
	}
	return 0
}

func (mf *maxFlow) flow(s, t int) int {
	flow := 0
	for {
		mf.bfs(s)
		if mf.lv[t] < 0 {
			return flow
		}
		for i := 0; i < len(mf.g); i++ {
			mf.iter[i] = 0
		}
		for {
			f := mf.dfs(s, t, mf.inf)
			if f == 0 {
				break
			}
			flow += f
		}
	}
}


Measured memory_bytes: 12688