洛谷4595[COCI2011-2012#5]Poplocavanje题解(AC自动机+重构)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/86254592

题目:luogu4595.
题目大意:给定一个字符串和另外一个字符串集合,求它有多少个字符不能被任何一个字符串集合中的字符串匹配.

看起来很裸的题,可以直接对字符串集合建立AC自动机,然后每个节点记录一个最深的可以通过fail指针跳到的打了字符串结尾的节点,就可以在匹配的时候保证时间复杂度.

但是我们发现AC自动机的内存太大,在只有64M的内存下根本无法通过.于是我们考虑每次读入了较多个字符串时匹配一次,然后把AC自动机清空继续读入,这样可以保证空间复杂度,但会牺牲一定的时间(然而这道题并不卡时).

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;
#define m(a) memset(a,0,sizeof(a))

const int C=26,N=500000,M=60;

struct Trie{
  int s[C],fail,cnt,get,deep;
  Trie(){m(s);fail=cnt=get=deep=0;}
}tr[N+9];
int cn,ans;

void Build(){tr[cn=0]=Trie();}

void Insert(char *c,int len){
  int x=0;
  for (int i=1;i<=len;++i)
    if (tr[x].s[c[i]-'a']) x=tr[x].s[c[i]-'a'];
    else {
      tr[x].s[c[i]-'a']=++cn;
      tr[cn]=Trie();
      tr[cn].deep=tr[x].deep+1;
      x=cn;
    }
  ++tr[x].cnt;
}

queue<int>q;

void Get_fail(){
  for (int i=0;i<C;++i)
    if (tr[0].s[i]) q.push(tr[0].s[i]);
  while (!q.empty()){
  	int x=q.front();q.pop();
  	tr[x].get=tr[x].cnt?x:tr[tr[x].fail].get;
  	for (int i=0;i<C;++i)
  	  if (tr[x].s[i]) tr[tr[x].s[i]].fail=tr[tr[x].fail].s[i],q.push(tr[x].s[i]);
  	  else tr[x].s[i]=tr[tr[x].fail].s[i];
  }
}

int b[N+9];

void Query(char *c,int len){
  int x=0,t;
  for (int i=1;i<=len;++i){
  	x=tr[x].s[c[i]-'a'];t=tr[x].get;
  	++b[i-tr[t].deep+1];--b[i+1];
  }
}

int n,m,len;
char c[N+9],tmp[N+9];

Abigail into(){
  scanf("%d",&n);
  scanf("%s",c+1);
  scanf("%d",&m);
  Build();
  for (int i=1;i<=m;++i){
  	scanf("%s",tmp+1);
  	len=strlen(tmp+1);
  	Insert(tmp,len);
	if (i%200==0){
	  Get_fail();
	  Query(c,n);
	  Build();
	}
  }
  Get_fail();
  Query(c,n);
}

Abigail outo(){
  int ans=0;
  for (int i=1;i<=n;++i){
    b[i]+=b[i-1];
    if (b[i]==0) ++ans;
  }
  printf("%d\n",ans);
}

int main(){
  into();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/86254592