给你一个矩阵,上面只有*和0(最大1000*1000),然后有t(最大100)组询问,每组询问给你一个小矩阵(最大50*50),问这t个矩阵中有多少个是在大矩阵中出现的。
思路:
这个题目做的有点蛋疼,说下我的第一个思路,我的想法是把所有的数据都按照行去压缩成longlong的,因为最大50位<64位 可以直接每行都压缩了,每个p*q的小矩阵就变成p个longlong的数字了,然后在把大矩阵同样处理,可以得到n*(m-q+1)个longlong的数字,然后就直接暴力的去KMP匹配,结果超时了,很是不解,反复改,优化,还是超时,最后没办法用了网上说的那个暴力,第一次还是超时了,看讨论,发现枚举的时候是i和j调换一下,按照算法的思路看时间复杂度没变,但是竟然ac了,MD,说实话,枚举的时间复杂度最坏是O(1000*1000*50*100),而KMP的时间复杂度是O(1000*(1000+50)*100)其实把i,j调换后AC了已经想到这个题目的数据的问题,我是在想不明白在概率的角度去考虑为什么横着枚举会比竖着枚举快(目前感觉就是后台数据的问题),但是如果是用KMP去跑的话把状态压缩改成竖着压缩应该能过,而且有可能会更快,这蛋疼数据。下面是暴力AC的代码和横着的KMP代码,暴力的好像是1500多AC的,横着的KMP超时了,有兴趣可以改成竖着试试,但是我不想改了,有点恶心了,那个KMP我优化了n多次,也重敲了n多次,对于这样的数据A不A已经不再重要。
状态压缩+暴力
#include<stdio.h>
long long hash1[1005][1005];
long long hash2[55];
bool jude(int n ,int m ,int p ,int q)
{
int i ,j ,k;
for(i = 1 ;i <= n - p + 1 ;i ++)
for(j = 1 ;j <= m - q + 1 ;j ++)
{
for(k = 1 ;k <= p ;k ++)
if(hash1[i+k-1][j] != hash2[k])
break;
if(k == p + 1) return 1;
}
return 0;
}
int main ()
{
int n ,m ,t ,p ,q ,cas = 1 ,i ,j;
char str[1005];
long long T ,O ,I = 1;
while(~scanf("%d %d %d %d %d" ,&n ,&m ,&t ,&p ,&q) && n + m + t + p + q)
{
for(i = 1 ;i <= n ;i ++)
{
scanf("%s" ,str);
T = 0;
for(j = 1 ;j <= m ;j ++)
{
O = str[j-1] == '*';
T = T * 2 + O;
if(j >= q)
{
hash1[i][j-q+1] = T;
if(str[j-q]=='*') T = T - (I<<(q-1));
}
}
}
int ans = 0;
while(t--)
{
for(i = 1 ;i <= p ;i ++)
{
scanf("%s" ,str);
T = 0;
for(j = 1 ;j <= q ;j ++)
{
O = str[j-1] == '*';
T = T * 2 + O;
}
hash2[i] = T;
}
if(p <= n && q <= m && jude(n ,m ,p ,q))
ans ++;
}
printf("Case %d: %d
" ,cas ++ ,ans);
}
return 0;
}
状态压缩+KMP
#include<stdio.h>
#include<string.h>
#define N 1000 + 10
#define M 50 + 5
long long hash1[N][N];
long long hash2[M];
int next[M];
int map[N][N];
void Get_Next(int m)
{
int j = 0 ,k = -1;
next[0] = -1;
while(j < m)
{
if(k == -1 || hash2[j] == hash2[k])
next[++j] = ++k;
else k = next[k];
}
return ;
}
int KMP(int n ,int m ,int J)
{
int i ,j;
for(i = j = 0 ;i < n ;)
{
if(hash1[i][J] == hash2[j])
{
if(j == m - 1) return 1;
i ++ ,j ++;
}
else
{
j = next[j];
if(j == -1)
j = 0 ,i ++;
}
}
return 0;
}
int main ()
{
int n ,m ,t ,p ,q ,i ,j ,cas = 1;
char str[N];
while(~scanf("%d %d %d %d %d" ,&n ,&m ,&t ,&p ,&q) && n + m + t + p + q)
{
for(i = 1 ;i <= n ;i ++)
{
scanf("%s" ,str);
for(j = 0 ;j < m ;j ++)
map[i][j+1] = str[j] == '*';
}
long long T ,O = 1;
if(p <= n && q <= m)
for(i = 1 ;i <= n ;i ++)
{
T = 0;
for(j = 1 ;j <= m ;j ++)
{
T = T * (long long)2 + (long long)map[i][j];
if(j >= q)
{
hash1[i-1][j-q] = T;
if(map[i][j-q+1]) T -= (O << (q - 1));
}
}
}
for(i = 0 ;i <= m ;i ++)
hash1[n][i] = -1;
int ans = 0;
while(t--)
{
for(i = 1 ;i <= p ;i ++)
{
scanf("%s" ,str);
T = 0;
for(j = 0 ;j < q ;j ++)
{
if(str[j] == '*') O = 1;
else O = 0;
T = T * 2 + O;
}
hash2[i-1] = T;
}
hash2[p] = -1;
if(p > n || q > m) continue;
Get_Next(p);
for(i = 0 ;i <= m - q ;i ++)
{
if(KMP(n ,p ,i))
{
ans ++;
break;
}
}
}
printf("Case %d: %d
" ,cas ++ ,ans);
}
return 0;
}