题意:
给定N×M,N,M≤300的矩阵,求最大的回文正方形的边长
分析:
现场过了120,我真是一口老血,明明挺难的一个题
赛后尼玛一看,卧槽O(n5)的算法过了,n2枚举点,n枚举边长,n2判断回文
赛上想对了,可是写法很绕,绕了我很久,尼玛Manacher倍增真吃空间,hdu真抠还卡空间
我想的这个正解真的是很麻烦,−−本来没想完,被队友提示二分就会了
p1[i][j]:=i行的以j为中心的回文半径,p2[i][j]:=i列的以j为中心的回文半径
−−被卡了空间,我重复利用了一下这个数组
至于求法,就每行来一遍Manacher,然后再每列来一遍(相当于把矩阵转置之后每行来一遍)
然后绕人的开始了−−,来看题目答案的这个正方形,插入了#是这个样的
123456789
1 #########
2 #2#3#3#2#
3 #########
4 #2#3#3#2#
5 #########
6 #2#3#3#2#
7 #########
8 #2#3#3#2#
9 #########
我们已经知道第5行每个#的 纵向 的回文半径长度,以及第5列每个#的 横向 的回文半径长度
显然通过行我们可以知道列回文长度(最小的那个),通过列我们可以知道行回文长度(最小的那个)
如何快速查询呢,−−显然区间RMQ,行列分别维护300∗2个SparseTable就好了
显然这个判断回文正方形的复杂度是O(logn)的
蓝儿我们要枚举点,枚举长度,然后在对应的2个st里查询RMQ,判断是否合法,这样复杂度是O(n3logn)
这样会T,不是我想要的,赛上我想到了这里,然后队友冷不丁冒出个二分
我说这没有单调性啊,然后立马想到奇偶分开。。。
卧槽,枚举左上角点,奇偶长度二分,然后复杂度瞬间就O(n2log2n)了,然后就可以AC了
其实之前还想到哈希判断的,但是我让他们写他们不写,因为子串太多,哈希碰撞会哭,蓝儿有这么过的
数据弱要寄刀片啊
代码:
//
// Created by TaoSama on 2016-02-25
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 3e2 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m, a[N][N], b[N][N];
int s[N << 1], p[N][N << 1];
void manacher(int *a, int *p, int n) {
s[0] = '@'; s[1] = '#';
int l = 2;
for(int i = 1; i <= n; ++i) {
s[l++] = a[i];
s[l++] = '#';
}
s[l] = 0;
int mx = 0, id;
for(int i = 1; i < l; ++i) {
if(mx > i) p[i] = min(mx - i, p[2 * id - i]);
else p[i] = 1;
while(s[i - p[i]] == s[i + p[i]]) ++p[i];
if(mx < p[i] + i) mx = p[i] + i, id = i;
}
}
struct SparseTable {
int n, dp[10][N << 1];
void init(int _n) {
n = _n;
for(int i = 1; (1 << i) <= n; ++i)
for(int j = 1; j + (1 << i) - 1 <= n; ++j)
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j + (1 << i - 1)]);
}
int RMQ(int l, int r) {
int k = 31 - __builtin_clz(r - l + 1);
return min(dp[k][l], dp[k][r - (1 << k) + 1]);
}
} hor[N << 1], ver[N << 1];
bool check(int x) {
int ret = 0;
for(int i = 1; i <= 2 * n + 1; i += 2) {
for(int j = 1; j <= 2 * m + 1; j += 2) {
int right = j + 2 * x, down = i + 2 * x;
if(right > 2 * m + 1 || down > 2 * n + 1) continue;
int h = hor[i + x].RMQ(j, right), v = ver[j + x].RMQ(i, down);
int cur = min(h, v);
if(cur - 1 >= x) {
return true;
}
}
}
return false;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
scanf("%d", &a[i][j]), b[j][i] = a[i][j];
//rows
for(int i = 1; i <= n; ++i) manacher(a[i], p[i], m);
for(int i = 1; i <= 2 * m + 1; ++i) {
for(int j = 1; j <= 2 * n + 1; ++j) {
int tmp = INF;
if(!(j & 1)) tmp = p[j / 2][i];
ver[i].dp[0][j] = tmp;
}
ver[i].init(2 * n + 1);
}
//columns
for(int i = 1; i <= m; ++i) manacher(b[i], p[i], n);
for(int i = 1; i <= 2 * n + 1; ++i) {
for(int j = 1; j <= 2 * m + 1; ++j) {
int tmp = INF;
if(!(j & 1)) tmp = p[j / 2][i];
hor[i].dp[0][j] = tmp;
}
hor[i].init(2 * m + 1);
}
int ans = 0;
{
int l = 0, r = 150;
while(l <= r) {
int m = l + r >> 1;
if(check(2 * m)) l = m + 1;
else r = m - 1;
}
--l;
ans = max(ans, 2 * l);
}
{
int l = 0, r = 150;
while(l <= r) {
int m = l + r >> 1;
if(check(2 * m + 1)) l = m + 1;
else r = m - 1;
}
--l;
ans = max(ans, 2 * l + 1);
}
printf("%d\n", ans);
}
return 0;
}