BZOJ 2462: [BeiJing2011]矩阵模板

1 篇文章 0 订阅
1 篇文章 0 订阅

Description

给定一个M行N列的01矩阵,以及Q个A行B列的01矩阵,你需要求出这Q个矩阵哪些在原矩阵中出现过。
所谓01矩阵,就是矩阵中所有元素不是0就是1。

Input

输入文件的第一行为M、N、A、B,参见题目描述。
接下来M行,每行N个字符,非0即1,描述原矩阵。
接下来一行为你要处理的询问数Q。
接下来Q个矩阵,一共Q*A行,每行B个字符,描述Q个01矩阵。

Output

你需要输出Q行,每行为0或者1,表示这个矩阵是否出现过,0表示没有出现过,1表示出现过。

HINT

对于100%的数据,A < = 100。

还是想不通为什么数据这么水。。。其实我的程序有个bug,今天考完试才发现,成绩发下来居然水过了,,无语。。
下面说说思路首先字符总数不大,而且字符串较多,且是01串,情况数较少,,一看就是AC自动机啊,对于每一个询问的子矩阵,每行为一个字符串,建一个AC自动机,然后将原矩阵的各行作为一个字符串进行匹配;每匹配一个字符串,就记录下字矩阵的改行是由原矩阵的哪行匹配成功的,,这里由于匹配的数量不知,因此用vector或者直接手动开个栈也行(毕竟数据太弱),,注意,除了字矩阵的第一行外,字矩阵其余行都要在该行的前一行与原矩阵当前行的前一行匹配成功时才记录下匹配信息;直到原矩阵的最后一行如果也匹配成功,,则该矩阵存在于原矩阵中。。

不知道你们有没有发现bug,,那就是匹配矩阵时我只记录了匹配成功的行数信息,保证了每一行都是连续的,,却没有保证列的连续,,因此匹配时有可能发生错位。只要在记录行数的同时再记录一下列数就能解决这个问题了。
以下代码未经修改,,但BZOJ也能水过,有兴趣的可以自己把bug填好。(反正劳资没兴趣。你们随意。)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
struct node { 
    int ch[2],file;vector<int> count;
    node() { file=ch[0]=ch[1]=0; } 
}t[1000010];
int head,ans,m,n,a,tot,b,dl[101][1001];char str[1001][1001];
void insert(char str[],int j) {
    int now=head;int i=0;
    while(i<b) {
      int d=str[i]-'0';
      if(t[now].ch[d]==0) t[now].ch[d]=tot++;
      now=t[now].ch[d];i++;
    } t[now].count.push_back(j);return;
}
void bfs() {
    int now=head,point=1; queue<int> q; t[head].file=head;
    for(int i=0;i<=1;i++) {
      if(t[head].ch[i]) {
        q.push(t[head].ch[i]);
        t[t[head].ch[i]].file=head;
      }
      else t[head].ch[i]=head;
    }
    while(!q.empty()) {
      int x=q.front();q.pop();
      for(int i=0;i<=1;i++) {
        if(t[x].ch[i]) {
          q.push(t[x].ch[i]);point=t[x].file;
          while(t[point].ch[i]==0&&point!=head) point=t[now].file;
          t[t[x].ch[i]].file=t[point].ch[i];
        }
        else t[x].ch[i]=t[t[x].file].ch[i];
      }
    }
}
void mark(int now,int j) {
    int l=t[now].count.size();
    for(int i=0;i<l;i++) {
      int x=t[now].count[i];
      if(x==1) { 
        dl[x][++dl[x][0]]=j;
        if(x==a) ans=1;continue; 
      }
      if(dl[x-1][0]==0) continue;
      if(dl[x-1][dl[x-1][0]]==j-1||dl[x-1][dl[x-1][0]-1]==j-1) {
        dl[x][++dl[x][0]]=j;
        if(x==a) ans=1;
      }
    } return;
}
void find(char str[],int j) {
    int now=head,i=0;
    while(i<n) {
      int d=str[i++]-'0';
      if(t[now].ch[d]) now=t[now].ch[d];
      else {
        while(!t[now].ch[d]&&now!=head) now=t[now].file;now=t[now].ch[d];
      }
      if(t[now].count.size()) mark(now,j);
    }
}
int main() {
    scanf("%d%d%d%d",&m,&n,&a,&b);tot=1;
    for(int i=1;i<=m;i++) { scanf("%s",str[i]); }
    int q;scanf("%d",&q);
    if(b>n||a>m) { while(q--) printf("0\n");return 0; }
    for(int i=1;i<=q;i++) {
      ans=0;head=tot++;
      for(int j=0;j<=a;j++) dl[j][0]=0;
      for(int j=1;j<=a;j++) { scanf("%s",&str[0]);insert(str[0],j); } bfs();
      for(int j=1;j<=m;j++) { if(!ans) find(str[j],j); }
      printf("%d",ans);if(i!=q) printf("\n");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值