字符串哈希+二分+dp-Rectangle-HDU4495
题意:
T 组 测 试 用 例 , 每 组 包 括 一 个 n × m 的 由 小 写 字 母 组 成 的 矩 阵 。 定 义 “ 等 腰 直 角 三 角 形 ” : 直 角 边 平 行 于 矩 阵 的 边 , 且 关 于 直 角 角 平 分 线 对 称 , 即 每 一 层 直 角 边 均 相 同 。 要 计 算 整 个 矩 阵 中 , 最 大 的 等 腰 直 角 三 角 形 的 面 积 ( 字 母 数 量 ) 。 T组测试用例,每组包括一个n×m的由小写字母组成的矩阵。\\ \ \\定义“等腰直角三角形”:直角边平行于矩阵的边,且关于直角角平分线对称,即每一层直角边均相同。\\ \ \\要计算整个矩阵中,最大的等腰直角三角形的面积(字母数量)。 T组测试用例,每组包括一个n×m的由小写字母组成的矩阵。 定义“等腰直角三角形”:直角边平行于矩阵的边,且关于直角角平分线对称,即每一层直角边均相同。 要计算整个矩阵中,最大的等腰直角三角形的面积(字母数量)。
样例:
Sample Input:
1
4 4
abab
dacb
adab
cabb
Sample Output:`在这里插入代码片`
6
容 易 找 到 一 个 最 大 的 等 腰 直 角 三 角 形 , 直 角 顶 点 在 ( 2 , 1 ) 位 置 。 容易找到一个最大的等腰直角三角形,直角顶点在(2,1)位置。 容易找到一个最大的等腰直角三角形,直角顶点在(2,1)位置。
数据范围:
T ∈ [ 1 , 20 ] , n , m ∈ [ 1 , 500 ] 。 T i m e L i m i t : 100000 m s , M e m o r y L i m i t : 102400 K 。 T∈[1,20],n,m∈[1,500]。\\Time\ Limit:100000ms,Memory\ Limit:102400K。 T∈[1,20],n,m∈[1,500]。Time Limit:100000ms,Memory Limit:102400K。
题解:
①
、
根
据
直
角
的
位
置
来
枚
举
最
长
的
直
角
边
,
有
四
种
可
能
。
可
以
只
处
理
一
种
直
角
情
况
,
剩
下
的
三
种
可
以
通
过
把
矩
阵
旋
转
90
度
来
解
决
。
②
、
这
里
只
处
理
直
角
顶
点
在
“
右
下
方
”
的
情
况
,
设
顶
点
坐
标
为
(
i
,
j
)
,
m
l
e
n
[
i
]
[
j
]
为
该
顶
点
处
的
最
大
相
等
直
角
边
的
长
度
,
d
p
[
i
]
[
j
]
为
以
该
点
为
顶
点
,
所
能
构
成
的
最
大
等
腰
直
角
三
角
形
的
直
角
边
的
长
度
,
有
状
态
转
移
方
程
d
p
[
i
+
1
]
[
j
+
1
]
=
m
i
n
(
d
p
[
i
]
[
j
]
+
2
,
m
l
e
n
[
i
+
1
]
[
j
+
1
]
)
,
如
下
图
:
①、根据直角的位置来枚举最长的直角边,有四种可能。\\\qquad可以只处理一种直角情况,剩下的三种可以通过把矩阵旋转90度来解决。\\ \ \\②、这里只处理直角顶点在“右下方”的情况,设顶点坐标为(i,j),mlen[i][j]为该顶点处的最大相等直角边的长度,\\\qquad dp[i][j]为以该点为顶点,所能构成的最大等腰直角三角形的直角边的长度,\\\qquad有状态转移方程dp[i+1][j+1]=min(dp[i][j]+2,mlen[i+1][j+1]),如下图:
①、根据直角的位置来枚举最长的直角边,有四种可能。可以只处理一种直角情况,剩下的三种可以通过把矩阵旋转90度来解决。 ②、这里只处理直角顶点在“右下方”的情况,设顶点坐标为(i,j),mlen[i][j]为该顶点处的最大相等直角边的长度,dp[i][j]为以该点为顶点,所能构成的最大等腰直角三角形的直角边的长度,有状态转移方程dp[i+1][j+1]=min(dp[i][j]+2,mlen[i+1][j+1]),如下图:
③
、
那
么
每
一
趟
用
a
n
s
保
存
所
有
d
p
[
i
]
[
j
]
中
最
大
值
即
所
能
构
成
的
最
大
等
腰
直
角
三
角
形
的
边
长
,
再
将
矩
形
翻
转
90
°
做
同
样
的
操
作
,
共
执
行
4
次
。
③、那么每一趟用ans保存所有dp[i][j]中最大值即所能构成的最大等腰直角三角形的边长,\\\qquad再将矩形翻转90°做同样的操作,共执行4次。
③、那么每一趟用ans保存所有dp[i][j]中最大值即所能构成的最大等腰直角三角形的边长,再将矩形翻转90°做同样的操作,共执行4次。
④ 、 最 终 面 积 通 过 数 列 求 和 公 式 : ( a n s + 1 ) ( a n s ) 2 得 到 。 ④、最终面积通过数列求和公式:\frac{(ans+1)(ans)}{2}得到 。 ④、最终面积通过数列求和公式:2(ans+1)(ans)得到。
具体落实:
① 、 对 每 个 输 入 样 例 分 行 和 列 求 哈 希 值 保 存 到 h r 和 h c 中 。 ② 、 对 每 个 点 ( i , j ) 求 最 大 相 同 直 角 边 长 , 得 到 数 组 m l e n 。 这 一 部 分 用 哈 希 来 判 断 相 等 , 用 二 分 处 理 最 大 长 度 。 ③ 、 d p 求 每 个 点 作 为 直 角 顶 点 所 能 构 成 的 最 大 等 腰 直 角 三 角 形 的 边 长 , 用 a n s 保 存 所 有 结 果 中 的 最 大 值 。 ④ 、 矩 阵 旋 转 90 ° , 共 执 行 4 次 , 最 后 利 用 公 式 求 最 大 面 积 。 ①、对每个输入样例分行和列求哈希值保存到hr和hc中。\\\ \\②、对每个点(i,j)求最大相同直角边长,得到数组mlen。这一部分用哈希来判断相等,用二分处理最大长度。\\\ \\③、dp求每个点作为直角顶点所能构成的最大等腰直角三角形的边长,用ans保存所有结果中的最大值。\\ \ \\④、矩阵旋转90°,共执行4次,最后利用公式求最大面积。 ①、对每个输入样例分行和列求哈希值保存到hr和hc中。 ②、对每个点(i,j)求最大相同直角边长,得到数组mlen。这一部分用哈希来判断相等,用二分处理最大长度。 ③、dp求每个点作为直角顶点所能构成的最大等腰直角三角形的边长,用ans保存所有结果中的最大值。 ④、矩阵旋转90°,共执行4次,最后利用公式求最大面积。
注意:
①
、
处
理
每
一
列
的
哈
希
值
时
,
是
当
作
把
矩
阵
逆
时
针
旋
转
90
°
来
看
待
的
。
哈
希
的
区
间
如
下
图
:
①、处理每一列的哈希值时,是当作把矩阵逆时针旋转90°来看待的。哈希的区间如下图:
①、处理每一列的哈希值时,是当作把矩阵逆时针旋转90°来看待的。哈希的区间如下图:
②
、
旋
转
矩
阵
后
,
行
与
列
的
值
发
生
交
换
。
②、旋转矩阵后,行与列的值发生交换。
②、旋转矩阵后,行与列的值发生交换。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const int N=510;
const int base=131;
const int mod=1e9+7;
char s[N][N];
int n,m,T;
int mlen[N][N],dp[N][N],ans; ///mlen[i][j] :(i,j)所在位置最长对称直角边的长度
ll hr[N][N],hc[N][N],p[N];
ll get(ll h[],int l,int r)
{
return (h[r]-h[l-1]*p[r-l+1]%mod+mod)%mod;
}
void get_hash()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
hr[i][j]=(hr[i][j-1]*base%mod+s[i][j])%mod;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
hc[i][j]=(hc[i][j-1]*base%mod+s[j][i])%mod;
}
bool check(int i,int j,int len)
{
return (get(hr[i],j-len+1,j)==get(hc[j],i-len+1,i)) ? true : false ;
}
void cal() ///二分预处理mlen数组
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int l=1,r=min(i,j);
while(l<r)
{
int mid=(l+r+1)>>1;
if(!check(i,j,mid)) r=mid-1;
else l=mid;
}
mlen[i][j]=l;
}
}
void solve()
{
get_hash();
cal();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
dp[i][j]=min(dp[i-1][j-1]+2,mlen[i][j]);
ans=max(ans,dp[i][j]);
}
}
void Rotate()
{
int tmp[N][N];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
tmp[j][n-i+1]=s[i][j];
swap(n,m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s[i][j]=tmp[i][j];
}
int main()
{
p[0]=1;
for(int i=1;i<N;i++) p[i]=p[i-1]*base%mod;
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(int i=0;i<4;i++)
{
solve();
Rotate();
}
printf("%d\n",(ans+1)*ans/2);
}
return 0;
}