zoukankan      html  css  js  c++  java
  • 【SPOJ 8093】Sevenk Love Oimaster

    http://www.spoj.com/problems/JZPGYZ/
    查询一个询问串在上面n个串中多少个串的子串。
    后缀数组+主席树,常熟有点大。。。
    建出广义SAM,利用parent树的dfs序,每次相当于询问parent树的一个子树中出现了多少不同的颜色。
    可以用主席树统计,pre表示dfs序上与这个位置属于同一个串的前一个位置在哪,主席树询问区间内的pre小于这个区间左端点的个数(也就是不同的颜色数)。
    或者更方便地,把每个询问拆成parent树dfs序上的两个前缀和相减。
    离线排序前缀和从左到右用bits维护pre为权值的树状数组并且查询小于某个区间左端点的pre的个数,统计答案时做一下减法就可以了(类似主席树)。
    时间复杂度(O(nlog 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
    ", tmp->cnt);
    	}
    	return 0;
    }
    
  • 相关阅读:
    LeetCode18. 四数之和
    15. 三数之和
    LeetCode202. 快乐数
    LeetCode1. 两数之和
    LeetCode349. 两个数组的交集
    LeetCode242. 有效的字母异位词
    VSCode运行时弹出powershell
    关于cin, cin.get(), getchar(),getline()的字符问题
    剑指 Offer 27. 二叉树的镜像
    BFS zoj 1649
  • 原文地址:https://www.cnblogs.com/abclzr/p/6653471.html
Copyright © 2011-2022 走看看