/*
* Problem :: 16946 / 벽 부수고 이동하기 4
*
* Kind :: BFS + Dynamic Programming
*
* Insight
* - 칸 중에 벽인 곳은 빈칸으로 바꾸고 그 칸에서 매번 BFS 를 하려고 했는데...
* 이 경우, 같은 빈 칸을 또 BFS 하게 된다 (이 방식으로 알고리즘을 짜면 시간초과가 뜬다)
*
* - 벽칸에서 인접한 칸만으로 빈칸의 개수를 알 수 있지 않을까?
* + 11001 => 빈칸 넘버링 00110 => No. 1 : 2
* 00111 22000 No. 2 : 3
* 01010 20304 No. 3 : 1
* 10101 05060 No. 4 : 1
* No. 5 : 1
* + BFS 돌때, 빈칸 넘버링과 동시에 그 넘버에 해당하는 값을 저장한다면
* 나중에 벽칸근처의 빈칸의 개수를 그 칸에 인접한 빈칸의 넘버링만 알면 알 수 있다
*
* Point
* - 벽칸에 인접한 빈칸의 넘버가 중복될 수 있다
* Set 으로 이러한 경우를 다루었다
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
#include <set>
#include <map>
#include <queue>
using namespace std;
#define endl '\n'
// Set up : Global Variables
int N, M;
char board[1000][1000];
int label[1000][1000];
bool isVisited[1000][1000];
struct Point { int y, x; };
int dy[4] = {+1, 0, 0, -1};
int dx[4] = {0, +1, -1, 0};
// Set up : Functions Declaration
bool isValid(int y, int x);
int bfs(int sy, int sx, int No);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
cin >> N >> M;
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
cin >> board[i][j];
}
}
// Process
/* cnt[넘버] = 그 넘버에 해당하는 빈칸의 개수 */
map<int,int> cnt;
int No = 0;
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
/* 방문하지 않은 칸이고 빈칸이면 */
if (not(isVisited[i][j]) && board[i][j] == '0') {
/* BFS 를 돌면서 넘버링 해줌, 그 넘버에 해당하는 빈칸의 개수도 저장 */
cnt[No] = bfs(i, j, ++No);
}
}
}
// Control : Output
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
/* 빈칸이면 */
if (board[i][j] == '0') {
/* '0' 을 출력 */
cout << 0;
/* 벽이면 */
} else {
set<int> labels; /* 여기다 인접한 칸의 넘버값을 넣어준다 */
for (int k=0; k<4; k++) {
int ay = i + dy[k];
int ax = j + dx[k];
if (isValid(ay, ax)) {
labels.insert(label[ay][ax]);
}
}
int s = 1; /* 이동할 수 있는 칸은 자신의 칸도 포함 */
for (int l : labels) {
/* 넘버에 해당하는 빈칸의 개수를 더해줌 */
s += cnt[l];
} s %= 10;
cout << s;
}
} cout << endl;
} cout << endl;
}
// Helper Functions
bool isValid(int y, int x)
/* 주어진 좌표가 유효하면 true 를 반환, 그 외 false 를 반환 */
{
return y >= 0 && y < N && x >= 0 && x < M;
}
int bfs(int sy, int sx, int No)
/* (sy, sx) 를 기준으로 BFS 를 돎, 빈칸의 개수를 반환 */
{
queue<Point> que;
que.push({sy, sx});
isVisited[sy][sx] = true;
int size = 0;
while (not(que.empty())) {
Point c = que.front(); que.pop(); /* Queue 에서 원소를 꺼낼때마다 */
label[c.y][c.x] = No; /* 넘버링해주고 */
size++; /* 빈칸의 개수 증가시켜줌 */
for (int i=0; i<4; i++) {
int ny = c.y + dy[i];
int nx = c.x + dx[i];
/* 좌표가 유효하고, 방문하지 않았고, 빈칸이면 */
if (isValid(ny, nx) && not(isVisited[ny][nx]) && board[ny][nx] == '0') {
/* 방문처리 하고 */
isVisited[ny][nx] = true;
/* Queue 에 넣음 */
que.push({ny, nx});
}
}
}
return size;
}