题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3909
题意:给出两个矩阵A和B,找出最大的相同子矩阵S。输出S的高和宽以及在A和B中的位置。
思路:1、利用偏移量,其实就是枚举第二个矩阵跟第一个矩阵的那个位置开始对齐。再明白一点,就是,首先枚举B的(0,0)与A的哪个位置对齐;然后,枚举A的(0,0)与B的哪个位置对齐。这两个可以合并起来,偏移量范围为(-n2,-m2)-(n1,m1)。
2、然后就可以直接比较出对应位置的是不是相同。得到一个n2*m2的g矩阵,g[i][j]为0或者1。然后我们就是找这个g中最大的只包含1的子矩阵。我们一行一行处理,对于某一行i的某列j,我们用h[j]记录这列向上可匹配的长度,就是当前行的j列向上有连续的h[j]个1。然后,我们用l[j]和r[j]记录j列最左和最右匹配的长度。我们只需要从左向右扫描一遍就可求出l[i];然后从右向左扫描一遍就可求出r[j]数组。比如我们以计算l[j]为例。(1)对于当前的j,若j=0或者h[j-1]=0,那么left=j(left是一个临时变量);(2)若i=0(第一行)或者g[i-1][j]=0(上面一行为0),那么l[j]=left,因为这个l[j]是每行重复使用的,前面可能l[j]是记录的别的值,但是现在必须要置为当前的left,因为i=0和g[i-1][j]=0都等同于这是新的一行开始;(2)若left>l[j],l[j]=left。
3、从2的最后一句看出这个貌似是以h优先的。不妨设g的一种情况为
0 1 2 3 (列号)
0 0 1 1
1 1 1 1
1 1 1 1
显然最大是8.那么现在呢?我们转移完第一行后第二列的l为2,我们在2的最后一句是若left>l[j],l[j]=left。但是到达第二行是l[2]=0对吧,但是没有转移。此时求出的是6。但是这个程序并没有错。因为总有一个偏移量我们能够得到这个:
0 1 2 3 (列号)
1 1 1 1
1 1 1 1
此时就得到了8。所以,其实在一次中是以列优先的,也就是h。但是这并不会导致正确结果丢失,因为我们枚举的偏移量会覆盖每种情况。
int n1,m1,n2,m2;
char a[N][N],b[N][N];
int h[N],r[N],l[N],g[N][N];
int Max,X1,Y1,X2,Y2,rr,cc;
void deal(int ax,int ay)
{
if(ax+n2<0||ay+m2<0) return;
clr(h,0);
int i,j,k,left,right,n=n2,m=m2;
FOR0(i,n) FOR0(j,m)
{
if(a[i+ax+50][j+ay+50]==b[i][j]) g[i][j]=1;
else g[i][j]=0;
}
FOR0(i,n)
{
FOR0(j,m) g[i][j]?h[j]++:h[j]=0;
FOR0(j,m) if(h[j])
{
if(!j||!h[j-1]) left=j;
if(!i||!g[i-1][j]) l[j]=left;
if(left>l[j]) l[j]=left;
}
FORL0(j,m-1) if(h[j])
{
if(j==m-1||!h[j+1]) right=j;
if(!i||!g[i-1][j]) r[j]=right;
if(right<r[j]) r[j]=right;
}
FOR0(j,m) if((r[j]-l[j]+1)*h[j]>Max)
{
Max=(r[j]-l[j]+1)*h[j];
rr=h[j];
cc=r[j]-l[j]+1;
X1=i-h[j]+ax+1;
Y1=l[j]+ay;
X2=i-h[j]+1;
Y2=l[j];
}
}
}
int main()
{
Rush(n1)
{
RD(m1);
int i,j;
clr(a,0);
for(i=50;i<n1+50;i++) RD(a[i]+50);
RD(n2,m2);
FOR0(i,n2) RD(b[i]);
Max=0;
for(i=-n2;i<n1;i++) for(j=-m2;j<m1;j++)
{
deal(i,j);
}
if(!Max) puts("0 0");
else PR(rr,cc),PR(X1+1,Y1+1),PR(X2+1,Y2+1);
}
return 0;
}