Hash函数 + FloodFills
思路:
大体思路就是找所有的连通块(FooldFills算法(DFS))
然后对于每个连通块,判断是否是重复出现的形状,这是个难点
我们不可能把一个形状旋转个90°或者180°之类的
那么我们最好的方法就是把一个形状对应成一个尽量唯一的数
就是哈希表了,我们把一个形状映射成一个数的好处就是这个形状无论是那个方向,我们都不需要考虑,我们只需要考虑这个块本身,而不需要考虑这个块在图中的位置之类。
对于哈希表,我们知道最重要的是哈希函数,哈希函数需要解决的一个核心问题就是处理冲突,这里的哈希函数直接背过就行了:hash(set)= sum(C(n,2)),意思就是在这个连通块set中任选两个点(有C(n,2))中选择,计算他们距离的总和,注意这个总和是要开平方的,不然引起冲突的概率会很大。
那么对于每一个连通块,我们求他的哈希值,然后保存下来,方便以后的连通块查询,然后转变成小写字母就行了,需要注意的是,在比较的时候,由于距离开根号了,是个浮点数,所以我们直接等于,浮点数是可能存在误差的,所以我们要让他们两个相减的绝对值小于一个很小的数(1e-6/1e-8)就行了。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
const double eps = 1e-8;
int n, m, top;
char g[N][N];
PII q[N * N];//开N^2
//哈希表
double Hash[N];
int id;
double get_dist(PII a, PII b)
{
double dx = a.x - b.x;
double dy = a.y - b.y;
double res = sqrt(dx * dx + dy * dy);
return res;
}
double get_Hash()//sum:C(c,2)
{
double sum = 0;
for(int i = 0; i < top; i ++ )
for(int j = i + 1; j < top; j ++ )
sum += get_dist(q[i], q[j]);
return sum;
}
char get_id(double key)
{
// static double Hash[30]; //静态,全局变量,一共26个英文字母,所以大小开30即可
// static int id = 0;
for(int i = 0; i < id; i ++ )
if(fabs(Hash[i] - key) < eps) //如果重复出现,浮点数比较不能直接等于
return i + 'a';
Hash[id ++ ] = key; //没出现过,保存到哈希表中
return id - 1 + 'a';
}
void dfs(int a, int b)//八个方向
{
q[top ++ ] = {a, b};//放入集合中
g[a][b] = '0';//遍历过了直接在原图标记一下
for(int x = a - 1; x <= a + 1; x ++ )//遍历所有相邻的点
for(int y = b - 1; y <= b + 1; y ++ )
{
if(x == a && y == b) continue;//中心点跳过
if(x < 0 || x >= n || y < 0 || y >= m || g[x][y] != '1')
continue;//越界或者不合法跳过
dfs(x, y);
}
}
int main()
{
cin >> m >> n;
for(int i = 0; i < n; i ++ ) cin >> g[i];
// for(int i = 0; i < n; i ++ ) cout << g[i] << endl;
for(int i = 0; i < n; i ++ )
{
for(int j = 0; j < m; j ++ )
{
if(g[i][j] == '1')//遍历到一个连通块
{
top = 0;//清空之前连通块里面的所有点
dfs(i, j);//搜索当前连通块
char c = get_id(get_Hash());//通过哈希值得到id
// cout << "C: " << c << endl;
// cout << "sum: " << top << endl;
for(int k = 0; k < top; k ++ )
g[q[k].x][q[k].y] = c;//修改原图中的点
}
}
}
for(int i = 0; i < n; i ++ ) cout << g[i] << endl;
return 0;
}