1. 程式人生 > >洛谷4595[COCI2011-2012#5]Poplocavanje題解(AC自動機+重構)

洛谷4595[COCI2011-2012#5]Poplocavanje題解(AC自動機+重構)

題目: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; }