[BZOJ2462]矩阵模板(暴力||矩阵hash)

11 篇文章 0 订阅

题目描述

传送门

题解

朴素的暴力就能A掉,数据弱。
学习了一下矩阵hash。
感觉非常玄学;刚开始把每一行转化成2进制数,然后把所有的行合并起来转化成 2b 进制数,就得到了一个矩阵的hash值。
算出来所有小矩阵的hash值,然后把大矩阵里的每一个小矩阵的hash值也算出来,查找就行了。
很玄学的是%的数,貌似让它直接炸掉就可以了= =两次模的是一样的数?
TA说概率很科学。

代码

暴力

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int n,m,a,b,q;
int A[1005][1005],B[1005][1005];
bool flag;
char s[1005];

int main(){
    scanf("%d%d%d%d\n",&n,&m,&a,&b);
    for (int i=1;i<=n;++i){
        gets(s);
        for (int j=1;j<=m;++j)
          A[i][j]=s[j-1]-'0';
    }
    scanf("%d\n",&q);
    while (q--){

        for (int i=1;i<=a;++i){
            gets(s);
            for (int j=1;j<=b;++j)
              B[i][j]=s[j-1]-'0';
        }

        for (int i=1;i<=n-a+1;++i)
          for (int j=1;j<=m-b+1;++j){
            flag=true;
            for (int k=1;k<=a;++k)
              for (int l=1;l<=b;++l)
                if (A[i+k-1][j+l-1]!=B[k][l]){
                    flag=false;
                    k=a+1;
                    l=b+1;
                }

            if (flag) {
                i=n+1;
                j=m+1;
            }

          }

        if (flag) printf("1\n");
        else printf("0\n");
    }
}

hash

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
#define ll unsigned long long

const int max_n=1e3+5;

int n,m,a,b,q;
char s[max_n];
int A[max_n][max_n],B[max_n][max_n];

ll S;
ll mt[max_n][max_n],squ[max_n];
ll mi2[max_n],miS[max_n];
ll hash[max_n*max_n],temp;

int main(){
    scanf("%d%d%d%d\n",&n,&m,&a,&b);

    S=1<<b;
    int N=max(a,b);
    mi2[0]=1;
    for (int i=1;i<=N;++i)
      mi2[i]=mi2[i-1]*2;

    miS[0]=1;
    for (int i=1;i<=N;++i)
      miS[i]=miS[i-1]*S;



    for (int i=1;i<=n;++i){
        gets(s);
        for (int j=1;j<=m;++j)
          A[i][j]=s[j-1]-'0';
    }

    for (int i=1;i<=n;++i){

        ll ans=0;
        for (int j=1;j<=b;++j)
          ans=ans+A[i][j]*mi2[b-j];
        mt[i][1]=ans;

        for (int j=2;j<=m-b+1;++j){
            ll head=A[i][j-1];
            ll tail=A[i][j+b-1];
            ans=ans*2-head*mi2[b]+tail;
            mt[i][j]=ans;
        }
    }

    for (int i=1;i<=m-b+1;++i){

        ll ans=0;
        for (int j=1;j<=a;++j)
          ans+=mt[j][i]*miS[a-j];
        hash[++temp]=ans;

        for (int j=2;j<=n-a+1;++j){
            ll head=mt[j-1][i];
            ll tail=mt[j+a-1][i];
            ans=ans*S-head*miS[a]+tail;
            hash[++temp]=ans;
        }

    }

    temp=unique(hash+1,hash+temp+1)-hash-1;
    sort(hash+1,hash+temp+1);

    scanf("%d\n",&q);

    while (q--){

        if (n<a||m<b){
            puts("0");
            continue;
        }


        for (int i=1;i<=a;++i){
            gets(s);
            for (int j=1;j<=b;++j)
              B[i][j]=s[j-1]-'0';
        }

        for (int i=1;i<=a;++i){
            ll ans=0;
            for (int j=1;j<=b;++j)
              ans=ans+B[i][j]*mi2[b-j];
            squ[i]=ans;
        }


        ll ans=0;
        for (int i=1;i<=a;++i)
          ans+=squ[i]*miS[a-i];
        if (hash[ lower_bound(hash+1,hash+temp+1,ans)-hash ]==ans) puts("1");
        else puts("0");
    }
}

总结

%的数还是要搞懂一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值