给定一个M行N列的01矩阵,以及Q个A行B列的01矩阵,你需要求出这Q个矩阵哪些在
原矩阵中出现过。
所谓01矩阵,就是矩阵中所有元素不是0就是1。
给定一个M行N列的01矩阵,以及Q个A行B列的01矩阵,你需要求出这Q个矩阵哪些在
原矩阵中出现过。
所谓01矩阵,就是矩阵中所有元素不是0就是1。
输入文件的第一行为M、N、A、B,参见题目描述。
接下来M行,每行N个字符,非0即1,描述原矩阵。
接下来一行为你要处理的询问数Q。
接下来Q个矩阵,一共Q*A行,每行B个字符,描述Q个01矩阵。
你需要输出Q行,每行为0或者1,表示这个矩阵是否出现过,0表示没有出现过,1表
示出现过。
对于100%的数据,A < = 100。
Day4
题解:二维hash 或 kmp 算法
这道题上午测试的时候写的kmp,大体上走的路线是对的,但是在判断子矩阵是想麻烦了,于是后两组超时了。
在某些神犇的提示下,又想了想,然后改了改,就AC了
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m,a,b,q; char s[1003][1003],s1[1003][1003]; int next[1003][1003],pos[10000]; void calc(char x[],int k)//对于小矩阵的每一行都建立失配函数 { next[k][0]=-1; int j; for (int i=0;i<b;i++) { j=next[k][i]; while (j!=-1&&x[i]!=x[j]) j=next[k][j]; next[k][i+1]=++j; } } int makep(int x,char k[]) { int i=0; int j=0; while (i<m) { if (j==-1||k[i]==s1[x][j]) i++,j++; else j=next[x][j]; if (j==b) return true; } return false; } int makep1(char x[],int now,int k)//从x[]中的now位向后搜索,找到第一个可以匹配的位置,返回匹配的第一个位置 { int i=now; int j=0; while (i<m) { if (j==-1||x[i]==s1[k][j]) i++,j++; else j=next[k][j]; if (j==b) return i-b+1; } return -1; } int main() { scanf("%d%d%d%d",&n,&m,&a,&b); for (int i=1;i<=n;i++) scanf("%s",s[i]); scanf("%d",&q); for (int i=1;i<=q*a;i++) scanf("%s",s1[i]); for (int i=1;i<=q*a;i++) calc(s1[i],i); if (a==1) { for (int j=1;j<=q;j++)//如果小矩阵只有一行的话,直接让他与大矩阵的每行匹配就可以了,匹配上后就不用继续匹配了。 { bool p=false; for (int i=1;i<=n;i++) if (makep(j,s[i])) { printf("1\n"),p=true; break; } if (!p) printf("0\n"); } return 0; } else { for (int i=1;i<=q;i++) { bool pd=false; for (int j=1;j<=n;j++)//先枚举大矩阵的每一行,让小矩阵从当前行开始逐行匹配 { bool p=true; int now=0; while (p&&!pd) { for (int k=1;k<=a;k++) { pos[k]=makep1(s[j+k-1],now,a*(i-1)+k); if (pos[k]==-1)//如果某一行没有匹配上,那么说明当前行是不合法的,直接跳转到当前行的下一行,重新匹配 { p=false; break; } } if (!p) break; now=0; int num=0; for (int k=1;k<=a;k++) { if (pos[k]==pos[1]) num++;//有可能小矩阵的每一行都匹配上了,但是他们匹配上的位置不在同一列,那么这也是不合法的 now=max(now,pos[k]); //now 用来记录下一次当前行开始重新匹配的位置,因为前面的都已经匹配过了,也就是不可能存在合法的子矩阵了,那么直接从所有匹配中找出最靠后的位置,从那里进行匹配即可。 } if (num==a) { printf("1\n"); pd=true; break; } } if (pd) break; } if (!pd) printf("0\n"); } } }
昨天学习了一下hash 算法,今天调了一上午的hash 终于AC了。T_T
hash 方法:把大矩阵中所有可能的小矩阵都转换成一个数,存储在hash 表里,每次查表就好了。
那么如何把小矩阵转换成一个数呢?昨天请教了一下神犇,神犇说你先把小矩阵的每一行看成一个二进制,然后转换成十进制,这样你就得到了a个十进制数,然后你确定一个字符基s(就是想办法把那a个数,看成a位,然后把他们转化成一个s进制数,一般s进制可以是一个足够大的质数)。
然后傻逼的我,开始了漫长AC路。
先是TLE,于是重新写。(友情提示:这道题貌似用map 映射会很慢,会TLE,当然如果你足够神,代码时间复杂度足够优越,也可以试试)
然后开始各种WA,好不容易调过了小数据,结果一上大数据,就开始全出0。最后看学长的代码,发现貌似是unsigned long long 的问题,学长用的long long 就是对的,难道炸飞的方式也有影响?于是我开始各种乱搞,最终得出一个结论,就是unsigned int 或unsigned long long 是可以自然溢出的,所有它本身得到的就是一个已经取模后的结果,那么我们在存储时就直接让他炸飞好了,不用取模来防止自然溢出。但是如果用long long 如果炸飞了,会出现负数之类的,所有我们可以通过对一个大质数取模的方式来进行hash 。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define p 1000000007 using namespace std; int n,m,a,b,tot; char s[1003]; int mat[1003][1003],ok[1003][1003]; long long sum[1000],ch[1003][1003],sh[1003][1003],data[1000003],maxn; long long mi,base; int main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); scanf("%d%d%d%d",&n,&m,&a,&b); for (int i=0;i<n;i++) { scanf("%s",s); for (int j=0;j<m;j++) mat[i][j]=s[j]-'0'; } mi=1; base=1; for (int i=1;i<=b;i++) mi=(mi<<1)%p; for (int i=1;i<=a;i++) base=(base*mi)%p; for (int i=0;i<n;i++) { for (int j=1;j<=b;j++) ch[i][m-b]=((ch[i][m-b]<<1)+mat[i][m-j])%p;//先把后b位搞成一个二进制,即 mat[i][x]*2^0+mat[i][x+1]*2^1....mat[i][x+b-1]*2^b-1 ,这样之后便于转移 for (int j=m-b-1;j>=0;j--) { ch[i][j]=((ch[i][j+1]<<1)%p-mat[i][j+b]*mi+mat[i][j])%p;//ch[i][j]中存储的是从第j位开始向后B个字符转成十进制后的答案,每次把前一次的答案都左移一位,然后减去最靠后的一位×2^b,在加上第j位 } } for (int i=m-b;i>=0;i--) { for (int j=1;j<=a;j++) sh[n-a][i]=((sh[n-a][i]*mi)+ch[n-j][i])%p;//以2^b为字符基,处理方式与上面几乎相同。 data[tot++]=(sh[n-a][i]+p)%p; for (int j=n-a-1;j>=0;j--) { sh[j][i]=(sh[j+1][i]*mi-ch[j+a][i]*base+ch[j][i])%p; data[tot++]=(sh[j][i]+p)%p; } } sort(data,data+tot); tot=unique(data,data+tot)-data; int t; scanf("%d",&t); for (int i=1;i<=t;i++) { for (int j=0;j<a;j++) { scanf("%s",s); for (int k=0;k<b;k++) ok[j][k]=s[k]-'0'; } int ans=0; for (int j=a-1;j>=0;j--) for (int k=b-1;k>=0;k--) ans=((ans<<1)+ok[j][k])%p; if (data[lower_bound(data,data+tot,ans)-data]==ans) printf("1\n"); else printf("0\n"); } }
这样用unsigned int 也是对的
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define p 1000000007 using namespace std; int n,m,a,b,tot; char s[1003]; int mat[1003][1003],ok[1003][1003]; unsigned int sum[1000],ch[1003][1003],sh[1003][1003],data[1000003],maxn; unsigned int mi,base; int main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); scanf("%d%d%d%d",&n,&m,&a,&b); for (int i=0;i<n;i++) { scanf("%s",s); for (int j=0;j<m;j++) mat[i][j]=s[j]-'0'; } mi=1; base=1; for (int i=1;i<=b;i++) mi=mi<<1; for (int i=1;i<=a;i++) base=base*mi; for (int i=0;i<n;i++) { for (int j=1;j<=b;j++) ch[i][m-b]=(ch[i][m-b]<<1)+mat[i][m-j]; for (int j=m-b-1;j>=0;j--) { ch[i][j]=(ch[i][j+1]<<1)-mat[i][j+b]*mi+mat[i][j]; } } for (int i=m-b;i>=0;i--) { for (int j=1;j<=a;j++) sh[n-a][i]=(sh[n-a][i]*mi)+ch[n-j][i]; data[tot++]=sh[n-a][i]; for (int j=n-a-1;j>=0;j--) { sh[j][i]=sh[j+1][i]*mi-ch[j+a][i]*base+ch[j][i]; data[tot++]=sh[j][i]; } } sort(data,data+tot); tot=unique(data,data+tot)-data; int t; scanf("%d",&t); for (int i=1;i<=t;i++) { for (int j=0;j<a;j++) { scanf("%s",s); for (int k=0;k<b;k++) ok[j][k]=s[k]-'0'; } unsigned int ans=0; for (int j=a-1;j>=0;j--) for (int k=b-1;k>=0;k--) ans=(ans<<1)+ok[j][k]; if (data[lower_bound(data,data+tot,ans)-data]==ans) printf("1\n"); else printf("0\n"); } }