題意:
給定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;
}