POJ 3690 Constellations 哈希表 矩阵匹配

题目大意

给出一个n * m的矩阵,矩阵中只有一些*或者0
n <= 1000, m <= 1000
然后有t (t <= 100)个询问,每次询问给出一个p * q的矩阵
p,q是提前固定的数值。
问这些询问中能是大矩阵的子矩阵的有几个

Sample Input

3 3 2 2 2
*00
0**
*00

**
00

*0
**
3 3 2 2 2
*00
0**
*00

**
00

*0
0*
0 0 0 0 0

Sample Output

Case 1: 1
Case 2: 2

解题思路

因为小矩阵的长宽都给定了,所以可以直接 O(n2) 预处理出大矩阵中每个p*q的小矩阵的哈希值(最多n*m个)放入哈希表中,再用给的小矩阵查询即可。

预处理时先用pre[i][j]处理出第i行前j的字符的哈希值,再算出每个位置开始q个字符的哈希值,然后用sum[i][j]处理1至i行从第j个字符开始q个字符的哈希值,也就是前缀和,最后用两个前缀和相减(sum[i][j]-sum[i][j-p]*pow[p*c])即为以第i-r+1行第j个字符为左上角的p*q的矩阵的哈希值了。

注意哈希表模数要取6位以上的素数(999997,233333),不然要超时,亲身经历,wu~~~~

时间复杂度 O(n2)

这里写代码片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#define ll unsigned long long
using namespace std;

int getint()
{
    int i=0 ,f=1;char c;
    for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=1005,p=3,H=999997;
char s[N];
int T,n,m,q,r,c,ans;
ll cf[2505],pre[N][N],sum[N][N],val[N*N];
int tot,hsh[H+5],nxt[N*N];

void insert(ll x)
{
    int y=x%H;
    for(int v=hsh[y];v;v=nxt[v])
        if(val[v]==x)return;
    nxt[++tot]=hsh[y],hsh[y]=tot,val[tot]=x;
    return;
}

void find(ll x)
{
    int y=x%H;
    for(int v=hsh[y];v;v=nxt[v])
        if(val[v]==x)
        {
            ans++;
            return;
        }
    return;
}

void Init()
{
    memset(hsh,0,sizeof(hsh));
    memset(pre,0,sizeof(pre));
    memset(sum,0,sizeof(sum));
    memset(cf,0,sizeof(cf));
    ans=tot=0;
    ll x;
    m=getint(),q=getint(),r=getint(),c=getint();
    cf[0]=1;
    for(int i=1;i<=r*c;i++)cf[i]=cf[i-1]*p;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)
        {
            if(s[j]=='0')pre[i][j]=pre[i][j-1]*p;
            else pre[i][j]=pre[i][j-1]*p+1;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m-c+1;j++)
        {
            sum[i][j]=pre[i][j+c-1]-pre[i][j-1]*cf[c];
            sum[i][j]=sum[i-1][j]*cf[c]+sum[i][j];
        }
    }
    for(int i=r;i<=n;i++)
        for(int j=1;j<=m-c+1;j++)
        {
            x=sum[i][j]-sum[i-r][j]*cf[r*c];
            insert(x);
        }
}

void solve()
{
    ll x=0;
    for(int i=1;i<=r;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=c;j++)
        {
            if(s[j]=='0')x=x*p;
            else x=x*p+1;
        }
    }
    find(x);
}

int main()
{
    //freopen("lx.in","r",stdin);
    while(n=getint())
    {
        if(n==0)break;
        Init();
        while(q--)
            solve();
        cout<<"Case "<<++T<<": "<<ans<<'\n';
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值