// dfmt off
T lread(T=long)(){return readln.chomp.to!T;}T[] lreads(T=long)(long n){return iota(n).map!((_)=>lread!T).array;}
T[] aryread(T=long)(){return readln.split.to!(T[]);}void arywrite(T)(T a){a.map!text.join(' ').writeln;}
void scan(L...)(ref L A){auto l=readln.split;foreach(i,T;L){A[i]=l[i].to!T;}}alias sread=()=>readln.chomp();
void dprint(L...)(lazy L A){debug{auto l=new string[](L.length);static foreach(i,a;A)l[i]=a.text;arywrite(l);}}
static immutable MOD=10^^9+7;alias PQueue(T,alias l="b<a")=BinaryHeap!(Array!T,l);import std, core.bitop;
// dfmt on

void main()
{
    int N, M;
    scan(N, M);
    auto S = new string[](N);
    foreach (i; 0 .. N)
        S[i] = sread();

    auto g = mf_graph!int(N * M + 2);
    int s = N * M;
    int t = N * M + 1;
    foreach (i; 0 .. N)
        foreach (j; 0 .. M)
        {
            if (S[i][j] == '#')
                continue;
            int v = i * M + j;
            if ((i + j) % 2 == 0)
            {
                g.add_edge(s, v, 1);
            }
            else
            {
                g.add_edge(v, t, 1);
            }
        }
    foreach (i; 0 .. N)
        foreach (j; 0 .. M)
        {
            if ((i + j) % 2 || S[i][j] == '#')
                continue;
            int v0 = i * M + j;
            if (i && S[i - 1][j] == '.')
            {
                int v1 = (i - 1) * M + j;
                g.add_edge(v0, v1, 1);
            }
            if (j && S[i][j - 1] == '.')
            {
                int v1 = i * M + j - 1;
                g.add_edge(v0, v1, 1);
            }
            if (i + 1 < N && S[i + 1][j] == '.')
            {
                int v1 = (i + 1) * M + j;
                g.add_edge(v0, v1, 1);
            }
            if (j + 1 < M && S[i][j + 1] == '.')
            {
                int v1 = i * M + j + 1;
                g.add_edge(v0, v1, 1);
            }
        }
    g.flow(s, t).writeln();
    auto edges = g.edges();
    auto grid = S.to!(char[][]);
    foreach (e; edges)
    {
        if (e.from == s || e.to == t || e.flow == 0)
            continue;
        int i0 = e.from / M, j0 = e.from % M;
        int i1 = e.to / M, j1 = e.to % M;

        if (i0 == i1 + 1)
        {
            grid[i1][j1] = 'v';
            grid[i0][j0] = '^';
        }
        else if (j0 == j1 + 1)
        {
            grid[i1][j1] = '>';
            grid[i0][j0] = '<';
        }
        else if (i0 == i1 - 1)
        {
            grid[i0][j0] = 'v';
            grid[i1][j1] = '^';
        }
        else
        {
            grid[i0][j0] = '>';
            grid[i1][j1] = '<';
        }
    }
    foreach (row; grid)
    {
        writeln(row);
    }
}

struct mf_graph(Cap)
{
    import std.typecons : Tuple;

public:
    this(int n)
    {
        _n = n;
        g = new _edge[][](n);
    }

    int add_edge(int from, int to, Cap cap)
    {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = cast(int) pos.length;
        pos ~= Tuple!(int, int)(from, cast(int)(g[from].length));
        g[from] ~= _edge(to, cast(int)(g[to].length), cap);
        g[to] ~= _edge(from, cast(int)(g[from].length) - 1, 0);
        return m;
    }

    struct edge
    {
        int from, to;
        Cap cap, flow;
    }

    edge get_edge(int i)
    {
        int m = cast(int)(pos.length);
        assert(0 <= i && i < m);
        auto _e = g[pos[i][0]][pos[i][1]];
        auto _re = g[_e.to][_e.rev];
        return edge(pos[i][0], _e.to, _e.cap + _re.cap, _re.cap);
    }

    edge[] edges()
    {
        int m = cast(int)(pos.length);
        edge[] result;
        foreach (i; 0 .. m)
            result ~= get_edge(i);
        return result;
    }

    void change_edge(int i, Cap new_cap, Cap new_flow)
    {
        int m = cast(int)(pos.length);
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto _e = &g[pos[i][0]][pos[i][1]];
        auto _re = &g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t)
    {
        return flow(s, t, Cap.max);
    }

    Cap flow(int s, int t, Cap flow_limit)
    {
        import std.container : DList;
        import std.algorithm : min;

        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);

        auto level = new int[](_n), iter = new int[](_n);
        DList!int que;

        void bfs()
        {
            level[] = -1;
            level[s] = 0;
            que.clear();
            que.insertBack(s);
            while (!que.empty)
            {
                int v = que.front;
                que.removeFront();
                foreach (e; g[v])
                {
                    if (e.cap == 0 || level[e.to] >= 0)
                        continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t)
                        return;
                    que.insertBack(e.to);
                }
            }
        }

        Cap dfs(int v, Cap up)
        {
            if (v == s)
                return up;
            Cap res = 0;
            int level_v = level[v];
            for (; iter[v] < cast(int)(g[v].length); iter[v]++)
            {
                auto i = iter[v];
                auto e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0)
                    continue;
                Cap d = dfs(e.to, min(up - res, g[e.to][e.rev].cap));
                if (d <= 0)
                    continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up)
                    break;
            }
            return res;
        }

        Cap flow = 0;
        while (flow < flow_limit)
        {
            bfs();
            if (level[t] == -1)
                break;
            iter[] = 0;
            while (flow < flow_limit)
            {
                Cap f = dfs(t, flow_limit - flow);
                if (!f)
                    break;
                flow += f;
            }
        }
        return flow;
    }

    bool[] min_cut(int s)
    {
        import std.container : DList;

        auto visited = new bool[](_n);
        DList!int que;
        que.insertBack(s);
        while (!que.empty)
        {
            int p = que.front;
            que.removeFront();
            visited[p] = true;
            foreach (e; g[p])
            {
                if (e.cap && !visited[e.to])
                {
                    visited[e.to] = true;
                    que.insertBack(e.to);
                }
            }
        }
        return visited;
    }

private:
    struct _edge
    {
        int to, rev;
        Cap cap;
    }

    int _n;
    Tuple!(int, int)[] pos;
    _edge[][] g;
}


Measured memory_bytes: 7212