矩阵(matrix)
? 解题记录 ? ? 哈希 ?    2018-03-16 21:47:48    1092    0    0

【题目描述】

给出一个 n × m 的矩阵。让你从中发现一个最大的正方形。使得这样子的正方形在矩阵 中出现了至少两次。输出最大正方形的边长。

【输入】

第一行两个整数 n, m 代表矩阵的长和宽;

接下来 n 行,每行 m 个字符(小写字母) ,表示矩阵。

【输出】

输出一个整数表示满足条件的最大正方形的边长。

【输入样例】

5 10
ljkfghdfas
isdfjksiye
pgljkijlgp
eyisdafdsi
lnpglkfkjl

【输出样例】

3

【提示】

【数据规模】

对于 30%的数据,n,m≤100;

对于 100%的数据,n,m≤500。


对于本题,我们可以这样哈希:选择一个进制数base,一次从左到右从上到下给每一个方格都分配一个base^k。这样有什么好处呢?首先,这个哈希的正确性是对的,不会有重复。其次,如果要提取一个矩形的哈希值,我们只需要对矩阵做前缀和,减出这个矩阵对应的哈希值。但是不同位置的相同矩阵哈希值会不一样。那么我们只需要把它们都“移到左上角“就行了——乘以询问矩阵左上角base^k的逆元。我们就可以O(1)判断两个矩阵是否相同了。

但是n^3是没有办法卡过去的,这样我们想到二分正方形的边长,就可以变为O(n^2 * logn)

代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
const int maxn = 505, base[2] = {555, 233}, P[2] = {6662333, 100000007};
struct node {int k2, next;}e[maxn * maxn];
int head[6662333 + 5];
char s[maxn][maxn];
int n, m, larger, Bpow[2][maxn][maxn], Binv[2][maxn][maxn];
int Pref[2][maxn][maxn], now[2], key, cnt;

void adde(const int &u, const int &k2) {
	e[++cnt] = (node) {k2, head[u]};
	head[u] = cnt;
}

LL fpow(LL a, int b, int p) {
	LL ans = 1;
	while(b) {
		if(b & 1) ans = (ans * a) % p;
		b >>= 1, a = a * a % p;
	}
	return ans;
}

int GetMtr(int a, int b, int c, int d, int st) {
	int ans;
	ans = (Pref[st][c][d] - Pref[st][c][b - 1] - Pref[st][a - 1][d] + Pref[st][a - 1][b - 1] + P[st] * 2) % P[st];
	ans = 1LL * ans * Binv[st][a][b] % P[st];
	return ans;
}

bool Fdk(int k) {
	memset(head, 0, sizeof(head)), cnt = 0;
	for(register int i = 1; i <= n - k; ++i) {
		for(register int j = 1; j <= m - k; ++j) {
			now[0] = GetMtr(i, j, i + k, j + k, 0);
			now[1] = GetMtr(i, j, i + k, j + k, 1);
			for(register int u = head[now[0]]; u; u = e[u].next)
				if(now[1] == e[u].k2) return 1;
			adde(now[0], now[1]);
		}
	}
	return 0;
}

int DC(int l, int r) {
	while(l < r - 1) {
		int mid = l + r >> 1;
		if(Fdk(mid)) l = mid;
		else r = mid;
	}
	return l;
}

int main() {
	//freopen("test.in", "r", stdin);
	scanf("%d%d", &n, &m), larger = max(n, m);
	Bpow[0][0][m] = fpow(base[0], P[0] - 2, P[0]), Bpow[1][0][m] = fpow(base[1], P[1] - 2, P[1]);
	for(register int st = 0; st <= 1; ++st)	
		for(register int i = 1; i <= n; ++i) {
			Bpow[st][i][1] = 1LL * Bpow[st][i - 1][m] * base[st] % P[st], Binv[st][i][1] = fpow(Bpow[st][i][1], P[st] - 2, P[st]); 
			for(register int j = 2; j <= m; ++j) {
				Bpow[st][i][j] = 1LL * Bpow[st][i][j - 1] * base[st] % P[st];
				Binv[st][i][j] = fpow(Bpow[st][i][j], P[st] - 2, P[st]);
			}
		}
	for(register int i = 1; i <= n; ++i)
		scanf("%s", s[i] + 1);
	for(register int st = 0; st <= 1; ++st)
		for(register int i = 1; i <= n; ++i) {
			for(register int j = 1; j <= m; ++j) {
				Pref[st][i][j] = (Pref[st][i][j - 1] + Pref[st][i - 1][j] - Pref[st][i - 1][j - 1] + P[st]) % P[st];
				Pref[st][i][j] = (Pref[st][i][j] + 1LL * s[i][j] * Bpow[st][i][j] % P[st]) % P[st];
			}
		}
	printf("%d", DC(-1, min(n, m) + 1) + 1);
	return 0;
}



上一篇: [USACO DEC13]牛棒球

下一篇: BZOJ3165: [Heoi2013]Segment [李超树]

1092 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航