• 【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;
    }
    
  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/abclzr/p/6653471.html
Copyright © 2020-2023  润新知