码迷,mamicode.com
首页 > 其他好文 > 详细

例题3.16 矩阵匹配器 UVa11019

时间:2015-08-28 19:51:14      阅读:207      评论:0      收藏:0      [点我收藏+]

标签:二维hash   字符串   

1.题目描述:点击打开链接

2.解题思路:本题可以利用AC自动机解决,但是发现,这种方法时间效率比较低,个人推荐利用二维Hash来解决本题。经过OJ上测试,AC自动机的方法需要1s以上,而二维Hash只需要不到100ms!因此下面介绍如何用二维hash来解决本题。


首先,任何hash技术都需要给定一个函数,使得不同字符串经过计算得到的hash值产生的冲突越少越好。对于字符矩阵,我们一般利用二维hash来处理。虽然是二维,但原理和一维的类似,即首先把每一行的前j个字符进行一维hash,对前i行进行处理,得到i行1列的hash值,接下来对这i行1列hash值再次hash,最终得到的hash值就是(i,j)处的hash值。写成递推式如下:

技术分享

上式中,每一行进行hash时候,seed=q;每一列进行hash的时候,seed=p,下面举一个简单的例子来说明此方法。

假设现在的字符矩阵如下:

技术分享

对该矩阵按照上述公式计算每一格的hash值,可以得到下面的hash矩阵:

技术分享

通过观察不难发现,二维hash的确就是2个一维hash的复合过程。而且,如果p,q选择合适,可以保证(i,j)处的hash值就可以看做子矩阵(0,0)~(i,j)的hash值,而且只要子矩阵不相同,那么对应的hash值一定不同。下面我们就用矩阵右下角的hash值代表整个矩阵的hash值。


那么,如何从该矩阵中计算(i,j)~(i+x-1,j+y-1)这个子矩阵的hash值呢?还是利用最初的计算公式,只需要反复迭代使用,即可得到下述公式:

技术分享

这样,有了上述的分析,本题就不难解决了,首先计算出原始字符矩阵的hash矩阵,同时算出输入的P矩阵的hash值h,通过枚举x*y的子矩阵,并计算该子矩阵的hash值是否等于h,如果是,则ans++。最终即可得到答案。

本题的时间复杂度是O((N-x)*(M-y))。

3.代码:

#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define me(s)  memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
//typedef pair <int, int> P;

const int N=1101;
const int p=131;
const int q=1331;

ll Hash[N][N];
ll hv[110][110];
ll powp[N],powq[N];
char str[N];

void init()//初始化p^i,q^i的结果
{
    powp[0]=powq[0]=1;
    for(int i=1;i<N;i++)
    {
        powp[i]=powp[i-1]*p;
        powq[i]=powq[i-1]*q;
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    init();
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        me(Hash);me(hv);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",str+1);
            for(int j=1;j<=m;j++)
                Hash[i][j]=Hash[i-1][j]*p+Hash[i][j-1]*q-Hash[i-1][j-1]*p*q+str[j];//计算Hash矩阵
        }
        int x,y;
        scanf("%d%d",&x,&y);
        for(int i=1;i<=x;i++)
        {
            scanf("%s",str+1);
            for(int j=1;j<=y;j++)
                hv[i][j]=hv[i-1][j]*p+hv[i][j-1]*q-hv[i-1][j-1]*p*q+str[j];//计算P矩阵的hash值
        }
        ll h;
        int ans=0;
        for(int i=1;i<=n-x+1;i++)
            for(int j=1;j<=m-y+1;j++)//枚举x*y的子矩阵
        {
            h=Hash[i+x-1][j+y-1]-Hash[i-1][j+y-1]*powp[x]-Hash[i+x-1][j-1]*powq[y]+Hash[i-1][j-1]*powp[x]*powq[y];
            if(h==hv[x][y])ans++; //相等,则ans++
        }
        printf("%d\n",ans);
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

例题3.16 矩阵匹配器 UVa11019

标签:二维hash   字符串   

原文地址:http://blog.csdn.net/u014800748/article/details/48055941

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!