题意:给定2500个模式串,和长度为5,100,000的母串,问母串中包含多少个模式串。如果模式串s1是模式串s2的子串,而且s2被母串包含,则s1应被忽略。
思路:首先比较常规,对模式串建立AC自动机。然后用母串S在trie图上遍历,如果:
1、走到未被忽略的终止节点x,则将x标记为已经匹配,并且忽略掉所有目前匹配串的所有子串。
2、走到危险但非终止的节点y, 则沿着y的前缀指针链找到第一个终止节点x,将x标记为已经匹配,并且忽略掉所有目前匹配串的所有子串。
忽略的方法就是将x的所有前缀结点flag置为0,并在trie图中x到root之间的所有结点都如此处理一遍(相当于遍历当前串的所有子串)
最后统计匹配且未被忽略的模式串数目。代码中flag数组为1表示终止结点,为2表示危险但是非终止结点,为3表示已经匹配的结点。
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <cstdlib> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define INF 0x3fffffff #define N 5100005 int T,n,top,len; int t[N][26],fail[N],flag[N],f[N]; char s[N]; int newnode(){ top++; for(int i = 0;i<26;i++) t[top][i] = -1; return top; } void init(){ clc(fail,0); clc(flag,0); clc(f,0); for(int i = 0;i<26;i++) t[0][i] = 1; f[1] = 0; top = 0; newnode(); } void input(){ char ch; int i; len = 0; while((ch=getchar()) && ch!='\n'){ if(ch=='['){ scanf("%d",&i); ch = getchar(); while(i--) s[len++] = ch; getchar(); }else s[len++] = ch; } s[len] = '\0'; } void insert(char* s){ int i,r = 1; for(i = 0;s[i];i++){ if(t[r][s[i]-'A'] == -1){ t[r][s[i]-'A'] = newnode(); f[top] = r; } r = t[r][s[i]-'A']; } flag[r] = 1;//为1表示是终止结点 } void buildDFA(){ int i,now; queue<int> q; q.push(1); while(!q.empty()){ now = q.front(); q.pop(); for(i = 0;i<26;i++){ if(t[now][i] == -1) t[now][i] = t[fail[now]][i]; else{ fail[t[now][i]] = t[fail[now]][i]; q.push(t[now][i]); if(!flag[t[now][i]] && flag[t[fail[now]][i]] == 1) flag[t[now][i]] = 2;//为2表示是危险结点,但是不是终止结点 } } } } void update(int x){ while(x){ flag[x] = 0; x = fail[x]; } } void match(int x){ flag[x] = 3; update(fail[x]); x = f[x]; while(x>1){ update(x); x = f[x]; } } void search(char* s){ int i,tmp,r = 1; for(i = 0;s[i];i++){ r = tmp = t[r][s[i]-'A']; if(flag[tmp] == 1) match(tmp); else if(flag[tmp] == 2){ while(tmp && flag[tmp]==2) tmp = fail[tmp]; if(flag[tmp] == 1) match(tmp); } } } int main(){ scanf("%d",&T); while(T--){ int i,res=0; init(); scanf("%d",&n); getchar(); for(i = 0;i<n;i++){ input(); insert(s); } buildDFA(); input(); search(s); for(i = 1;i<=top;i++) res+=(flag[i]==3); printf("%d\n",res); } return 0; }