题目大意:给出一些字符串,给出一些询问,每次问当前串在源串中的几个中出现过。
思路:将所有源串建立广义后缀自动机。每次新的一个串的时候,把last清成root,往里面加的时候,如果last指针往下走的时候已经有节点了,就需要拓展一个新的节点出来,否则就不满足广义后缀自动机的性质。此外,每一个节点代表的不一定是一个串,可能代表的是多个串的子串,所以要在每个点后面挂链,来表示这个节点是属于哪几个串中的子串。后面的事情就比较简单了,把后缀树建立出来,弄出DFS序,离线处所有询问,变成在一段序列中出现过多少不同的数字,弄一个树状数组维护一下。
其实和喵星球上的点名是差不多的,但是那个题我用的后缀数组暴力的,这个题会T。
还有这个题坑爹的卡内存啊,字符集太大,直接建立肯定mle,用map的话就不能用指针,我这还是第一次用数组写啊。。DT死了。。。
CODE:
//#define _CRT_SECURE_NO_DEPRECATE #include <map> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 200010 using namespace std; struct Graph{ int head[MAX],total; int next[MAX],aim[MAX]; void Add(int x,int y) { next[++total] = head[x]; aim[total] = y; head[x] = total; } }G,repre; struct Ask{ pair<int,int> interval; int id; Ask(pair<int,int> _,int __):interval(_),id(__) {} Ask() {} bool operator <(const Ask &a)const { return interval.second < a.interval.second; } }ask[MAX]; int len[MAX],father[MAX]; map<int,int> tranc[MAX]; int cnt,root,last; inline int NewNode(int _) { len[++cnt] = _; return cnt; } void Initialize() { root = last = NewNode(0); } inline void Add(int c,int i) { int p = tranc[last][c]; if(p) { if(len[p] == len[last] + 1) last = p; else { int rep = NewNode(len[last] + 1); father[rep] = father[p]; father[p] = rep; tranc[rep] = tranc[p]; for(int temp = last; temp && tranc[temp][c] == p; temp = father[temp]) tranc[temp][c] = rep; last = rep; } } else { int np = NewNode(len[last] + 1); for(p = last; p && !tranc[p][c]; p = father[p]) tranc[p][c] = np; if(!p) father[np] = root; else { int q = tranc[p][c]; if(len[q] == len[p] + 1) father[np] = q; else { int nq = NewNode(len[p] + 1); father[nq] = father[q]; father[q] = father[np] = nq; tranc[nq] = tranc[q]; for(; p && tranc[p][c] == q; p = father[p]) tranc[p][c] = nq; } } last = np; } repre.Add(last,i); } pair<int,int> subtree[MAX]; int seq[MAX],_clock; void DFS(int x) { seq[++_clock] = x; subtree[x].first = _clock; for(int i = G.head[x]; i; i = G.next[i]) DFS(G.aim[i]); subtree[x].second = _clock; } char s[MAX << 1]; inline pair<int,int> Find() { int now = root,length = strlen(s); for(int i = 0; i < length; ++i) { if(!tranc[now][s[i]]) return make_pair(-1,-1); now = tranc[now][s[i]]; } return subtree[now]; } int fenwick[MAX]; inline void Fix(int x,int c) { for(; x <= cnt; x += x&-x) fenwick[x] += c; } inline int GetSum(int x) { int re = 0; for(; x; x -= x&-x) re += fenwick[x]; return re; } int strings,asks; int last_add[MAX],ans[MAX]; int main() { Initialize(); scanf("%d%d",&strings,&asks); for(int i = 1; i <= strings; ++i) { scanf("%s",s); int length = strlen(s); last = root; for(int j = 0; j < length; ++j) Add(s[j],i); } for(int i = 1; i <= cnt; ++i) if(father[i]) G.Add(father[i],i); DFS(1); for(int i = 1; i <= asks; ++i) { scanf("%s",s); ask[i] = Ask(Find(),i); } sort(ask + 1,ask + asks + 1); int now = 1; while(ask[now].interval.first == -1 && now <= asks) ++now; for(int i = 1; i <= cnt; ++i) { for(int j = repre.head[seq[i]]; j; j = repre.next[j]) { int col = repre.aim[j]; Fix(i,1); if(last_add[col]) Fix(last_add[col],-1); last_add[col] = i; } for(; ask[now].interval.second == i; ++now) ans[ask[now].id] = GetSum(ask[now].interval.second) - GetSum(ask[now].interval.first - 1); } for(int i = 1; i <= asks; ++i) printf("%d\n",ans[i]); return 0; }