洛谷4595[COCI2011-2012#5]Poplocavanje題解(AC自動機+重構)
阿新 • • 發佈:2019-01-13
題目: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;
}