http://www.spoj.com/problems/JZPGYZ/
查询一个询问串在上面n个串中多少个串的子串。
后缀数组+主席树,常熟有点大。。。
建出广义SAM,利用parent树的dfs序,每次相当于询问parent树的一个子树中出现了多少不同的颜色。
可以用主席树统计,pre表示dfs序上与这个位置属于同一个串的前一个位置在哪,主席树询问区间内的pre小于这个区间左端点的个数(也就是不同的颜色数)。
或者更方便地,把每个询问拆成parent树dfs序上的两个前缀和相减。
离线排序前缀和从左到右用bits维护pre为权值的树状数组并且查询小于某个区间左端点的pre的个数,统计答案时做一下减法就可以了(类似主席树)。
时间复杂度\(O(n\log n)\)。
还有不科学的暴力的做法,对于n个模板串,暴力在parent树上打标记,时间复杂度\(O(n^2)\)。
数据并没有卡这种做法。于是我本着复习广义后缀自动机的原则(和懒得写bits的原则),只写了这种暴力的做法。。。
将近花了一天重新脑补了一下广义SAM,感觉以前直接套用普通SAM的插入模板导致多出来了一些点不美观(当然多出来的这些点都可以合并到它们的parent),就重写了一下不会多出来点的广义SAM模板,常数更大,代码更长qwq,但保证所有节点的Right集合都是它们parent的Right集合的真子集(及不用费心想着哪些点可以合并在一起了)。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 360003; char s[N]; int l[N], r[N]; struct State { State *par, *go[50]; int val, tim, cnt; State(int _num) : val(_num) { par = 0; tim = cnt = 0; memset(go, 0, sizeof(go)); } } *root, *last; void extend(int w) { if (last->go[w] && last->go[w]->val == last->val + 1) {last = last->go[w]; return;} State *p = last; State *np = new State(p->val + 1); while (p && p->go[w] == 0) p->go[w] = np, p = p->par; if (p == 0) np->par = root; else { State *q = p->go[w]; if (q->val == p->val + 1) np->par = q; else if (p != last) { State *nq = new State(p->val + 1); memcpy(nq->go, q->go, sizeof(q->go)); nq->par = q->par; q->par = np->par = nq; while (p && p->go[w] == q) p->go[w] = nq, p = p->par; } else { memcpy(np->go, q->go, sizeof(q->go)); np->par = q->par; q->par = np; while (p && p->go[w] == q) p->go[w] = np, p = p->par; } } last = np; } void mark(State *t, int timing) { if (t == root || t->tim == timing) return; t->tim = timing; ++t->cnt; mark(t->par, timing); } int n, m; int main() { root = new State(0); scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { l[i] = r[i - 1] + 1; scanf("%s", s + l[i]); r[i] = l[i] + strlen(s + l[i]) - 1; } for (int i = 1; i <= n; ++i) { last = root; for (int j = l[i], top = r[i]; j <= top; ++j) extend(s[j] - 'a'); } State *tmp; for (int i = 1; i <= n; ++i) { tmp = root; for (int j = l[i], top = r[i]; j <= top; ++j) { tmp = tmp->go[s[j] - 'a']; mark(tmp, i); } } int len; bool flag; for (int i = 1; i <= m; ++i) { scanf("%s", s); len = strlen(s); tmp = root; flag = true; for (int j = 0; j < len; ++j) if (tmp->go[s[j] - 'a']) tmp = tmp->go[s[j] - 'a']; else { flag = false; break; } if (!flag) puts("0"); else printf("%d\n", tmp->cnt); } return 0; }
来源:https://www.cnblogs.com/abclzr/p/6653471.html