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); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。