Two images A
and B
are given, represented as binary, square matrices of the same size. (A binary matrix has only 0s and 1s as values.)html
We translate one image however we choose (sliding it left, right, up, or down any number of units), and place it on top of the other image. After, the overlap of this translation is the number of positions that have a 1 in both images.git
(Note also that a translation does not include any kind of rotation.)github
What is the largest possible overlap?數組
Example 1:ide
Input: A = [[1,1,0], [0,1,0], [0,1,0]] B = [[0,0,0], [0,1,1], [0,0,1]] Output: 3 Explanation: We slide A to right by 1 unit and down by 1 unit.
Notes: 函數
1 <= A.length = A[0].length = B.length = B[0].length <= 30
0 <= A[i][j], B[i][j] <= 1
這道題給了咱們兩個用大小相同的二維數組表示的圖像,裏面只有0或1,問咱們通過任意平移後,能產生的最大重疊是多少,這裏只計算值爲1的重疊。給的例子中,咱們只要將圖像A向右和向下平移一位,就能獲得3個重疊。那麼首先來思考 brute force 的方法,對於一個 nxn 大小的數組,其實其能平移的狀況是有限的,水平和豎直方向分別有n種移動方式,那麼總共有 nxn 種移動方法,那麼咱們只要對於每種移動方式後,都計算一下重疊的個數,那麼就必定能夠找出最大值來。須要注意的是,A和B分別都須要移動 nxn 次,咱們可使用一個子函數來專門統計重疊個數,須要傳入橫向縱向的平移量 rowOffset 和 colOffset,那麼只需讓其中一個數組減去偏移量後跟另外一個數組對應位置的值相乘,因爲只有0和1,若相乘爲1的話,就說明有重疊,直接累加便可,參見代碼以下:post
解法一:優化
class Solution { public: int largestOverlap(vector<vector<int>>& A, vector<vector<int>>& B) { int res = 0, n = A.size(); for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { res = max(res, max(count(A, B, i, j), count(B, A, i, j))); } } return res; } int count(vector<vector<int>>& A, vector<vector<int>>& B, int rowOffset, int colOffset) { int sum = 0, n = A.size(); for (int i = rowOffset; i < n; ++i) { for (int j = colOffset; j < n; ++j) { sum += A[i][j] * B[i - rowOffset][j - colOffset]; } } return sum; } };
咱們還能夠換一種思路,因爲只有值爲1的地方纔有可能重疊,因此咱們只關心A和B中值爲1的地方,將其座標位置分別存入兩個數組 listA 和 listB 中。因爲對於A和B中的任意兩個1的位置,確定有一種方法能將A平移到B,平移的方法就是橫向平移其橫座標之差,豎向平移其縱座標之差。因爲其是一一對應關係,因此只要是橫縱座標差相同的兩對兒位置,必定是在同一次平移上。那麼咱們就須要一個 HashMap 來創建座標差值和其出現次數之間的映射,爲了降維,將橫縱座標之差轉爲字符串,而後中加上個橫槓分隔開,這樣只要組成了相同的字符串,那麼必定就是在同一個平移上,計數器自增1。最後在 HashMap 中找到最大的值便可,參見代碼以下:this
解法二:url
class Solution { public: int largestOverlap(vector<vector<int>>& A, vector<vector<int>>& B) { int res = 0, n = A.size(); vector<vector<int>> listA, listB; unordered_map<string, int> diffCnt; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (A[i][j] == 1) listA.push_back({i, j}); if (B[i][j] == 1) listB.push_back({i, j}); } } for (auto a : listA) { for (auto b : listB) { ++diffCnt[to_string(a[0] - b[0]) + "-" + to_string(a[1] - b[1])]; } } for (auto diff : diffCnt) { res = max(res, diff.second); } return res; } };
咱們能夠優化一下空間,能夠將二維座標加碼成一個數字,通常的作法都是將 (i, j) 變成 i*n + j,可是這道題卻不行,由於咱們算橫縱座標的差值時想直接相減,這種加碼方式會使得橫縱座標之間互相干擾。因爲題目中給了n的範圍,不會超過 30,因此咱們能夠給橫座標乘以 100,再加上縱座標,即 i*100 + j,這種加碼方式萬無一失。而後仍是要用 HashMap 來創建座標差值和其出現次數之間的映射,不過此次就簡單多了,不用轉字符串了,直接用數字相減便可,最後返回 HashMap 中最大的統計數,參見代碼以下:
解法三:
class Solution { public: int largestOverlap(vector<vector<int>>& A, vector<vector<int>>& B) { int res = 0, n = A.size(); vector<int> listA, listB; unordered_map<int, int> diffCnt; for (int i = 0; i < n * n; ++i) { if (A[i / n][i % n] == 1) listA.push_back(i / n * 100 + i % n); if (B[i / n][i % n] == 1) listB.push_back(i / n * 100 + i % n); } for (int a : listA) { for (int b : listB) { ++diffCnt[a - b]; } } for (auto diff : diffCnt) { res = max(res, diff.second); } return res; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/835
參考資料:
https://leetcode.com/problems/image-overlap/
https://leetcode.com/problems/image-overlap/discuss/177485/Java-Easy-Logic
https://leetcode.com/problems/image-overlap/discuss/130623/C%2B%2BJavaPython-Straight-Forward
https://leetcode.com/problems/image-overlap/discuss/138976/A-generic-and-easy-to-understand-method