• @loj



    @description@

    ION 每年规定一个命名串,要求每道题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同。
    由于一些特殊的原因,小 A 得到了 ION2017 的命名串。
    现在小 A 有 Q 次询问:每次给定 ION2017 的命名串和 ION2018 的命名串,求有几种题目的命名,使得这个名字是 ION2018 的命名串的一个非空连续子串且一定不会和 ION2017 的任何一道题目的名字相同。
    由于一些特殊原因,所有询问给出的 ION2017 的命名串都是某个串的连续子串。

    输入格式
    从文件 name.in 读入数据。
    第一行一个字符串 S,之后询问给出的 ION2017 的命名串都是 S 的连续子串。
    第二行一个正整数 Q,表示询问次数。
    接下来 Q 行,每行有一个字符串 T 和两个正整数 l, r,表示询问如果 ION2017 的命名串是 S[l, r],ION2018 的命名串是 T 的话,有几种命名方式一定满足规定。
    保证输入中给出的字符串都是由小写字母构成的。

    输出格式
    输出到文件 name.out 中。
    输出 Q 行,第 i 行一个非负整数表示第 i 个询问的答案。

    样例输入 1
    scbamgepe
    3
    smape 2 7
    sbape 3 8
    sgepe 1 9
    样例输出 1
    12
    10
    4

    数据范围与提示
    对于所有数据,保证 1 <= l <= r <= |S| <= 510^5, 1 <= |T| <= 510^5, ∑|T| <= 10^6, Q <= 10^5。

    @solution@

    先考虑 l = 1, r = |S| 的情况。

    我们不妨对 S 建出后缀自动机,然后把 T 拿到 S 上跑一跑。对于 T 的每一个前缀 i,我们都求出一个最大的 f[i],使得 T 的前缀 i 的长度为 f[i] 的后缀是 S 的子串。
    则显然前缀 i 长度 <= f[i] 的所有后缀也在 S 中出现过。

    接着我们对 T 建出后缀自动机,然后把每一个前缀对应的结点上打上 f[i] 的 tag,然后沿着 parent 树传递 tag。
    这样子就可以处理 T 的后缀自动机中每个结点对应的字符串有多少没有在 S 中出现。于是就可以解决题目的询问。

    然后考虑给定的串是 S 的某个子串 S[l...r] 时,我们一样是考虑对于每个 i 求出 f[i]。
    我们考虑使用可持久化线段树合并,求出 S 的后缀自动机上每一个结点的 end-pos 集合。

    则当 T 的前缀 i 匹配上了 S 后缀自动机的结点 x,如果要尽可能在 S[l...r] 中匹配成功,我们肯定是选择 end-pos 集合中 <= r 且尽量大的那个。线段树上二分一下即可。
    如果不存在这样一个 end-pos 或是这个 end-pos 对应的字符串超出了左端点,我们就当作匹配失败,继续沿着 parent 树跳。

    这样下来,因为要用线段树,所以时间复杂度是 O(|T|*logn) 的。

    @accepted code@

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN = 1000000;
    struct segtree{
    	struct node{
    		int ch[2], mx;
    	}pl[20*MAXN + 5];
    	int ncnt;
    	segtree() {ncnt = 0; pl[0].mx = -1;}
    	void pushup(int x) {
    		if( pl[x].ch[1] ) pl[x].mx = pl[pl[x].ch[1]].mx;
    		else pl[x].mx = pl[pl[x].ch[0]].mx;
    	}
    	int merge(int a, int b) {
    		if( !a ) return b;
    		if( !b ) return a;
    		int c = (++ncnt);
    		pl[c].ch[0] = merge(pl[a].ch[0], pl[b].ch[0]);
    		pl[c].ch[1] = merge(pl[a].ch[1], pl[b].ch[1]);
    		pushup(c);
    		return c;
    	}
    	int insert(int a, int l, int r, int p) {
    		int b = (++ncnt); pl[b].ch[0] = pl[a].ch[0], pl[b].ch[1] = pl[a].ch[1];
    		if( l == r ) {
    			pl[b].mx = l;
    			return b;
    		}
    		int mid = (l + r) >> 1;
    		if( p <= mid ) pl[b].ch[0] = insert(pl[a].ch[0], l, mid, p);
    		else pl[b].ch[1] = insert(pl[a].ch[1], mid + 1, r, p);
    		pushup(b);
    		return b;
    	}
    	int query(int x, int l, int r, int p) {
    		if( l == r ) return pl[x].mx;
    		int mid = (l + r) >> 1;
    		if( p <= mid ) return query(pl[x].ch[0], l, mid, p);
    		else {
    			int k = query(pl[x].ch[1], mid + 1, r, p);
    			if( k != -1 ) return k;
    			else return pl[pl[x].ch[0]].mx;
    		}
    	}
    };
    struct SAM{
    	struct node{
    		node *ch[26], *fa;
    		int mx, pos, tag;
    	}pl[2*MAXN + 5], *lst, *root, *ncnt;
    	SAM() {ncnt = lst = root = &pl[0];}
    	void clear() {
    		int size = ncnt - pl + 1;
    		for(int i=0;i<size;i++) {
    			for(int j=0;j<26;j++)
    				pl[i].ch[j] = NULL;
    			pl[i].fa = NULL, pl[i].mx = pl[i].pos = pl[i].tag = 0;
    		}
    		ncnt = lst = root = &pl[0];
    	}
    	void extend(int c) {
    		node *cur = (++ncnt), *p = lst; lst = cur;
    		cur->mx = cur->pos = p->mx + 1;
    		while( p && p->ch[c] == NULL )
    			p->ch[c] = cur, p = p->fa;
    		if( !p )
    			cur->fa = root;
    		else {
    			node *q = p->ch[c];
    			if( p->mx + 1 == q->mx )
    				cur->fa = q;
    			else {
    				node *cne = (++ncnt); (*cne) = (*q);
    				cne->mx = p->mx + 1; cne->pos = 0;
    				cur->fa = q->fa = cne;
    				while( p && p->ch[c] == q )
    					p->ch[c] = cne, p = p->fa;
    			}
    		}
    	}
    	node *a[2*MAXN + 5]; int b[2*MAXN + 5];
    	segtree T; int rt[2*MAXN + 5];
    	void sort(int len) {
    		int size = ncnt - pl + 1;
    		for(int i=1;i<=len;i++) b[i] = 0;
    		for(int i=0;i<size;i++) b[pl[i].mx]++;
    		for(int i=1;i<=len;i++) b[i] += b[i-1];
    		for(int i=0;i<size;i++) a[--b[pl[i].mx]] = &pl[i];
    	}
    	void build(int len) {
    		int size = ncnt - pl + 1; sort(len);
    		for(int i=0;i<size;i++)
    			rt[i] = 0;
    		for(int i=size-1;i>0;i--) {
    			if( a[i]->pos )
    				rt[a[i]-pl] = T.insert(rt[a[i]-pl], 1, len, a[i]->pos);
    			rt[a[i]->fa-pl] = T.merge(rt[a[i]->fa-pl], rt[a[i]-pl]);
    		}
    	}
    	long long solve(int len) {
    		int size = ncnt - pl + 1; sort(len);
    		long long ret = 0;
    		for(int i=size-1;i>0;i--)
    			a[i]->fa->tag = min(a[i]->fa->mx, max(a[i]->fa->tag, a[i]->tag)), ret += max(0, a[i]->mx - max(a[i]->fa->mx, a[i]->tag));
    		return ret;
    	}
    	void debug(int len) {
    		int size = ncnt - pl + 1;
    		for(int i=0;i<size;i++) {
    			printf("%d : %d %d %d %d
    ", i, pl[i].fa-pl, pl[i].mx, pl[i].pos, pl[i].tag);
    			//T.debug(rt[i], 1, len);
    		}
    		puts("");
    	}
    }S, T;
    char str[MAXN + 5];
    int mx[MAXN + 5];
    int main() {
    	freopen("name.in", "r", stdin);
    	freopen("name.out", "w", stdout);
    	scanf("%s", str);
    	int lenS = strlen(str);
    	for(int i=0;i<lenS;i++)
    		S.extend(str[i] - 'a');
    	S.build(lenS);
    	int Q; scanf("%d", &Q);
    	for(int i=1;i<=Q;i++) {
    		int l, r, lenT;
    		scanf("%s%d%d", str, &l, &r), lenT = strlen(str);
    		SAM::node *nw = S.root; int tmp = 0, x;
    		for(int j=0;j<lenT;j++) {
    			while( nw && nw->ch[str[j]-'a'] == NULL )
    				nw = nw->fa;
    			if( nw ) {
    				tmp = min(tmp, nw->mx) + 1, nw = nw->ch[str[j]-'a'];
    				x = S.T.query(S.rt[nw-S.pl], 1, lenS, r);
    				while( nw ) {
    					if( x == -1 ) nw = nw->fa;
    					else {
    						if( nw == S.root || l + nw->fa->mx <= x ) break;
    						else nw = nw->fa;
    					}
    					if( nw ) x = S.T.query(S.rt[nw-S.pl], 1, lenS, r);
    				}
    				if( nw )
    					tmp = min(tmp, min(x - l + 1, nw->mx));
    				else tmp = 0, nw = S.root;
    			}
    			else tmp = 0, nw = S.root;
    			mx[j] = tmp;
    		}
    		for(int j=0;j<lenT;j++)
    			T.extend(str[j]-'a');
    		nw = T.root;
    		for(int j=0;j<lenT;j++) {
    			nw = nw->ch[str[j]-'a'];
    			nw->tag = mx[j];
    		}
    		printf("%lld
    ", T.solve(lenT));
    		T.clear();
    	}
    }
    

    @details@

    因为要对 T 多次建后缀自动机,所以每次询问后要注意清空 T 的后缀自动机。

  • 相关阅读:
    web-----------HTTP协议
    python基础作业------模拟实现一个ATM + 购物商城程序
    python--------进程与线程
    作业--用户输入数字0-100,判断成绩,用函数
    blog真正的首页
    blog首页视图
    让django完成翻译,迁移数据库模型
    创建Django博客的数据库模型
    创建blog APP
    在PyCharm上创建Django项目
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11361871.html
Copyright © 2020-2023  润新知