前不久帮朋友做一个东西,里面涉及到一个核心算法。在一个AxB的大棋盘上,每个格子里有一个整数,现在有一个CxD的小棋盘,里面每个格子里也有一个整数。现在要求在大棋盘上找是否有和小棋盘匹配的区域,要求可以对称匹配(即小棋盘是大棋盘上相关区域的镜像也可以)。因为棋盘都不大,大的可能最多16x16,小的可能最多5x5,所以随手写了一个,现在把核心代码记录下来,以后有机会再改改。
Direction.java
package matchCore;
public enum Direction{
East,
West,
South,
North,
EastMirror,
WestMirror,
SouthMirror,
NorthMirror
}
PosAndDir.java
package matchCore;
public class PosAndDir {
public Position pos;
public Direction dir;
public PosAndDir() {
pos = new Position(0, 0);
dir = Direction.South;
}
public PosAndDir(int x, int y, Direction d) {
pos.x = x;
pos.y = y;
dir = d;
}
}
Position.java
package matchCore;
public class Position {
public int x;
public int y;
public Position(int a, int b) {
x = a;
y = b;
}
public boolean equal(Position pos) {
if (x == pos.x && y == pos.y) return true;
return false;
}
}
以上三个类都是辅助定义类,用来定义位置和方向。
IntArrayMatch.java
package matchCore;
import java.util.ArrayList;
import java.util.List;
public class IntArrayMatch {
// 找出所有匹配的位置和方向
public List<PosAndDir> matchAll(int[][] base, int[][] conf) {
if (base == null || conf == null) return null;
if (base.length < 1 || conf.length < 1) return null;
int xdim = conf.length;
int ydim = conf[0].length;
List<PosAndDir> list = new ArrayList<PosAndDir>();
for (int i = 0; i < base.length; i++) {
for (int j = 0; j < base[i].length; j++) {
for (Direction dir : Direction.values()) {
PosAndDir pd = new PosAndDir(i, j, dir);
int[][] subarray = getSubArray(base, pd, xdim, ydim);
if (subarray == null) continue;
else if (subMatch(subarray, conf)) list.add(pd);
}
}
}
return list;
}
// 找出一个匹配的位置和方向
public PosAndDir matchOne(int[][] base, int[][] conf) {
if (base == null || conf == null) return null;
if (base.length < 1 || conf.length < 1) return null;
int xdim = conf.length;
int ydim = conf[0].length;
PosAndDir pd = new PosAndDir();
for (int i = 0; i < base.length; i++) {
for (int j = 0; j < base[i].length; j++) {
pd.pos = new Position(i,j);
for (Direction dir : Direction.values()) {
pd.dir = dir;
int[][] subarray = getSubArray(base, pd, xdim, ydim);
if (subarray == null) continue;
else if (subMatch(subarray, conf)) return pd;
}
}
}
return null;
}
// 获取sub中值为tag的格子所在的所有坐标,base为盘面,pd为sub的起始坐标和方向
public List<Position> getPos(int[][] base, PosAndDir pd, int[][] sub, int tag) {
int xdim = sub.length;
int ydim = sub[0].length;
List<Position> m_pos = new ArrayList<Position>();
if (pd.dir == Direction.South) {
if (pd.pos.x + xdim > base.length) return null;
if (pd.pos.y + ydim > base[0].length) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0+i, y0+j);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.SouthMirror) {
if (pd.pos.x + xdim > base.length) return null;
if (pd.pos.y + ydim > base[0].length) return null;
int x0 = pd.pos.x + xdim - 1;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0-i, y0+j);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.East) {
if (pd.pos.x - ydim < -1) return null;
if (pd.pos.y + xdim > base[0].length) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0-j, y0+i);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.EastMirror) {
if (pd.pos.x - ydim < -1) return null;
if (pd.pos.y + xdim > base[0].length) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y + xdim - 1;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0-j, y0-i);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.North) {
if (pd.pos.x - xdim < -1) return null;
if (pd.pos.y - ydim < -1) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0-i, y0-j);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.NorthMirror) {
if (pd.pos.x - xdim < -1) return null;
if (pd.pos.y - ydim < -1) return null;
int x0 = pd.pos.x - xdim + 1;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0+i, y0-j);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.West) {
if (pd.pos.x + ydim > base.length) return null;
if (pd.pos.y - xdim < -1) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0+j, y0-i);
m_pos.add(pos);
}
}
}
return m_pos;
} else if (pd.dir == Direction.WestMirror) {
if (pd.pos.x + ydim > base.length) return null;
if (pd.pos.y - xdim < -1) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y - xdim + 1;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
if (sub[i][j] == tag) {
Position pos = new Position(x0+j, y0+i);
m_pos.add(pos);
}
}
}
return m_pos;
} else {
}
return null;
}
// 根据位置和方向pd给出base中大小为x,y的子矩阵
private int[][] getSubArray(int[][] base, PosAndDir pd, int xdim, int ydim) {
if (base == null || pd == null) {
}
if (xdim < 1 || ydim < 1) {
}
int[][] sub = new int[xdim][ydim];
if (pd.dir == Direction.South) {
if (pd.pos.x + xdim > base.length) return null;
if (pd.pos.y + ydim > base[0].length) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0+i][y0+j];
}
}
return sub;
} else if (pd.dir == Direction.SouthMirror) {
if (pd.pos.x + xdim > base.length) return null;
if (pd.pos.y + ydim > base[0].length) return null;
int x0 = pd.pos.x + xdim - 1;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0-i][y0+j];
}
}
return sub;
} else if (pd.dir == Direction.East) {
if (pd.pos.x - ydim < -1) return null;
if (pd.pos.y + xdim > base[0].length) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0-j][y0+i];
}
}
return sub;
} else if (pd.dir == Direction.EastMirror) {
if (pd.pos.x - ydim < -1) return null;
if (pd.pos.y + xdim > base[0].length) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y + xdim - 1;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0-j][y0-i];
}
}
return sub;
} else if (pd.dir == Direction.North) {
if (pd.pos.x - xdim < -1) return null;
if (pd.pos.y - ydim < -1) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0-i][y0-j];
}
}
return sub;
} else if (pd.dir == Direction.NorthMirror) {
if (pd.pos.x - xdim < -1) return null;
if (pd.pos.y - ydim < -1) return null;
int x0 = pd.pos.x - xdim + 1;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0+i][y0-j];
}
}
return sub;
} else if (pd.dir == Direction.West) {
if (pd.pos.x + ydim > base.length) return null;
if (pd.pos.y - xdim < -1) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0+j][y0-i];
}
}
return sub;
} else if (pd.dir == Direction.WestMirror) {
if (pd.pos.x + ydim > base.length) return null;
if (pd.pos.y - xdim < -1) return null;
int x0 = pd.pos.x;
int y0 = pd.pos.y - xdim + 1;
for (int i = 0; i < xdim; i++) {
for (int j = 0; j < ydim; j++) {
sub[i][j] = base[x0+j][y0+i];
}
}
return sub;
} else {
}
return null;
}
// 判断两个矩阵是否全同,全同返回true,否则返回false
private boolean subMatch(int[][] src, int[][] des) {
if (src == null || des == null) return false;
if (src.length != des.length) return false;
if (src.length < 1) return false;
if (src[0].length != des[0].length) return false;
for (int i = 0; i < src.length; i++) {
for (int j = 0; j < src[i].length; j++) {
if (src[i][j] != des[i][j]) return false;
}
}
return true;
}
}
上面这个类实现了匹配的主要功能。只需要在外面调用其public方法即可判断是否匹配,并且给出第一个匹配的区域的位置和方向。下面给出测试文件:
IntArrayMatchTest.java
package matchCore;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class IntArrayMatchTest {
private IntArrayMatch m_match;
private int[][] m_base;
private int[][] m_conf;
@Before
public void setUp() throws Exception {
m_match = new IntArrayMatch();
m_base = new int[9][9];
m_conf = new int[2][3];
int c = 0;
for (int j = 8; j >= 0; j--) {
for (int i = 0; i < 9; i++) {
c++;
m_base[i][j] = c;
System.out.print(m_base[i][j] + " ");
}
System.out.println();
}
m_conf[0][0] = 32;
m_conf[0][1] = 33;
m_conf[0][2] = 34;
m_conf[1][0] = 41;
m_conf[1][1] = 42;
m_conf[1][2] = 43;
}
@Test
public void testMatch() {
PosAndDir pd = m_match.matchOne(m_base, m_conf);
System.out.println("pos = (" + pd.pos.x + ", " + pd.pos.y +
"), dir = " + pd.dir.toString());
assertEquals(5, 5);
}
}
上面的单测结果为:
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45
46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72
73 74 75 76 77 78 79 80 81
pos = (4, 5), dir = West