HDU 4618 Palindrome Sub-Array 解题报告

题目

2013 多校第二场 总结

题意:

有一个全由数字组成的矩阵,你要找到一个最大的子正方形,且这个正方形每一行每一列都是回文串。

解法一:

先假设正方形的边长len为奇数,那么对于中心点(x,y),所有的(x-len/2,y)~(x+len/2,y)(y同理)都要是至少回文半径为len/2+1的回文串。如果我们事先用manacher求出所有的行和列的回文半径的话,用rmq就知道(x-len/2,y)~(x+len/2,y)是否满足要求。据说不用rmq直接一个个验证更快。

len是偶数咋办?像manacher一样,填充原矩阵,就不用管奇偶的问题了,但是中心点所在的行列要么都是填充的,要么都不是填充的。


//Time:296ms
//Memory:17960KB
//Length:2940B
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define MAXN 620
#define INF 1007
using namespace std;
int arr[MAXN][MAXN],tarr[MAXN];
short rad[2][MAXN][MAXN];
short rmq[2][MAXN][MAXN][10];
int ilog[MAXN];
void manacher(int a[],short r[],int len)
{
    for(int i=0;i<len;++i)  r[i]=0;
    r[0]=1;
    for(int i=1,j=0,k;i<len;)
    {
        while(a[i-j-1]==a[i+j+1])   ++j;
        r[i]=j;
        for(k=1;k<=j&&r[i-k]!=r[i]-k;++k)
            r[i+k]=min((int)r[i-k],r[i]-k);
        i+=k;
        j=max(0,j-k);
    }
}
void calrmq(int a[],short r[][10],int len)
{
    int fe=log(len)/log(2)+1;
    for(int i=0;i<len;++i)  r[i][0]=a[i];
    for(int i=1,j=1;i<fe;++i,j*=2)
        for(int k=0;k<len;++k)
            if(k+j<len)
                r[k][i]=min(r[k][i-1],r[k+j][i-1]);
            else    r[k][i]=r[k][i-1];
}
bool check(int x,int y,int len)
{
    int fe=ilog[len*2],ji;
    short tlen=INF;
    ji=1<<fe;
    tlen=min(tlen,min(rmq[0][y][x-len][fe],rmq[0][y][x+len-ji+1][fe]));
    tlen=min(tlen,min(rmq[1][x][y-len][fe],rmq[1][x][y+len-ji+1][fe]));
    return tlen>=len;
}
int main()
{
    //freopen("/home/moor/Code/input","r",stdin);
    int ncase,n,m,ans,nn,mm,tmp;
    scanf("%d",&ncase);
    for(int i=1;i<MAXN;++i)
        ilog[i]=log(i*1.0)/log(2.0);
    while(ncase--)
    {
        ans=0;
        scanf("%d%d",&n,&m);
        nn=n*2+3,mm=m*2+3;
        memset(arr,-1,sizeof(arr));
        for(int i=0;i<n;++i)
            for(int j=0;j<m;++j)
                scanf("%d",&arr[(i+1)*2][(j+1)*2]);
        n=n*2+3,m=m*2+3;
        for(int i=0;i<m;++i)
            arr[0][i]=-2,arr[n-1][i]=-3;
        for(int i=0;i<n;++i)
            arr[i][0]=-4,arr[i][m-1]=-5;
        arr[0][0]=-10,arr[n-1][0]=-11,arr[0][m-1]=-12,arr[n-1][m-1]=-13;

        for(int i=0;i<n;++i)
            manacher(arr[i],rad[0][i],m);
        for(int i=0;i<m;++i)
        {
            for(int j=0;j<n;++j)    tarr[j]=arr[j][i];
            manacher(tarr,rad[1][i],n);
        }
        for(int i=0;i<m;++i)
        {
            for(int j=0;j<n;++j)    tarr[j]=rad[0][j][i];
            calrmq(tarr,rmq[0][i],n-1);
        }
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)    tarr[j]=rad[1][j][i];
            calrmq(tarr,rmq[1][i],m-1);
        }
        for(int i=1;i<n;++i)
            for(int j=1;j<m;++j)
            {
                if((j%2==1)&&(i%2==0)||(j%2==0)&&(i%2==1))  continue;
                int l=1,r=min(min(i-1,n-i),min(j-1,m-j)),mid;
                while(l<r)
                {
                    if(ans>=r)   break;
                    mid=(l+r+1)/2;
                    if(check(i,j,mid))     l=mid;
                    else    r=mid-1;
                }
                ans=max(ans,l);
            }
        printf("%d\n",ans);
    }
    return 0;
}

解法二:

二分(奇偶)+hash判断是否对称,其实好暴力的,但是竟然比上面的快,比赛没敢敲==

代码:

//time:     109ms
//length:   2299B
//memory:   3504K
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 305

int n,m,a[maxn][maxn];
long long int p[maxn],hash_r[2][maxn][maxn],hash_c[2][maxn][maxn];
void hash()
{
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
            hash_r[0][i][j]=hash_r[0][i][j-1]*131+a[i][j];
        for (int j=m;j>=1;j--)
            hash_r[1][i][j]=hash_r[1][i][j+1]*131+a[i][j];
    }
    for (int i=1;i<=m;i++)
    {
        for (int j=1;j<=n;j++)
            hash_c[0][j][i]=hash_c[0][j-1][i]*131+a[j][i];
        for (int j=n;j>=1;j--)
            hash_c[1][j][i]=hash_c[1][j+1][i]*131+a[j][i];
    }
}
long long int calr0(int r,int x,int y) {return hash_r[0][r][y]-hash_r[0][r][x-1]*p[y-x+1];}
long long int calr1(int r,int x,int y) {return hash_r[1][r][x]-hash_r[1][r][y+1]*p[y-x+1];}
long long int calc0(int c,int x,int y) {return hash_c[0][y][c]-hash_c[0][x-1][c]*p[y-x+1];}
long long int calc1(int c,int x,int y) {return hash_c[1][x][c]-hash_c[1][y+1][c]*p[y-x+1];}
bool check(int l)
{
    for (int i=1;i+l-1<=n;i++)
        for (int j=1;j+l-1<=m;j++)
        {
            bool flag=true;
            for (int k=i;k<=i+l-1&&flag;k++)
                if (calr0(k,j,j+l/2-1)!=calr1(k,j+l/2+l%2,j+l-1))
                    flag=false;
            for (int k=j;k<=j+l-1&&flag;k++)
                if (calc0(k,i,i+l/2-1)!=calc1(k,i+l/2+l%2,i+l-1)) flag=false;
            if (flag) return true;
        }
    return false;
}
int main()
{
    //freopen("/home/christinass/code/in.txt","r",stdin);
    p[0]=1;
    for (int i=1;i<=300;i++) p[i]=p[i-1]*131;
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        hash();
        int l=0,r=n/2,ans=1;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            if (check(2*mid+1)) ans=2*mid+1,l=mid+1;
            else r=mid-1;
        }
        l=(ans+1)/2,r=n/2;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            if (check(2*mid)) ans=2*mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(HDU 4618 Palindrome Sub-Array 解题报告)