• HDU70847 Pty loves string


    传送门


    一句话题意:给你一个字符串(S)(q)组询问,每次给定两个数(x,y),求由长度为(x)的前缀和长度为(y)的后缀拼接而成的字符串在(S)中的出现次数。


    这题比赛的时候我差一点就做出来了,卡在了二维数点上。

    不过正解比我简单一些,因为只有一个模式串,所以不用SAM或AC自动机了,用kmp就行。

    那么,对于一个匹配位置([i, i + x + y - 1]),就有(S_{1 dots x}=S_{i dots i + x - 1},S_{j dots j + y - 1} = S_{|S| - y + 1 dots |S|}),且有(i + x = j).

    如果做过P5829 【模板】失配树这道题,很容易想到第一个条件就是fail树上(x)的子树。而如果把原串反过来,那么第二个条件就是反串的fail树上(y)的子树。

    于是问题就变成了,给你两棵树,每次询问两个子树中编号相同的点的个数,即二维数点问题。

    对于二维数点问题,我们可以采用在线的主席树做法或者离线后扫描线+树状数组的做法。这里用主席树求解。

    我们先把第一棵树的dfs序求出来,那么(x)的子树就是一段连续的dfs序。把第一棵树每个节点的dfs序映射到第二棵树上,就相当于在第二棵树(y)的子树中求有多少个节点的dfs序在([dfn[x], dfn[x] + size[x] - 1])中,而这个,用基于dfs序的主席树就可以做出来。总而言之,其主要思想就是让一维有序,另一维用数据结构维护和查找。


    比赛的时候我用SAM想到了类似fail树的构建方法,但是因为没有再用反串构建一遍fail树,所以二维数点不是判断相等,而是差值等于(y),就不知道怎么维护了。

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const int maxn = 2e5 + 5;
    const int maxt = 4e6 + 5;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    int n, m;
    char s[maxn];
    vector<int> V1[maxn], V2[maxn];
    
    int f[maxn];
    In void kmp_init()
    {
    	f[1] = 0, V1[0].push_back(1);
    	for(int i = 2, j = 0; i <= n; ++i)
    	{
    		while(j && s[j + 1] != s[i]) j = f[j];
    		if(s[j + 1] == s[i]) ++j;
    		f[i] = j;
    		V1[j].push_back(i);
    	}
    	reverse(s + 1, s + n + 1);
    	f[1] = 0, V2[0].push_back(1);
    	for(int i = 2, j = 0; i <= n; ++i)
    	{
    		while(j && s[j + 1] != s[i]) j = f[j];
    		if(s[j + 1] == s[i]) ++j;
    		f[i] = j;
    		V2[j].push_back(i);			//反串坐标与原来相反 
    	}
    }
    
    int siz1[maxn], dfn1[maxn], cnt1 = 0;
    In void dfs1(int now)
    {
    	siz1[now] = 1, dfn1[now] = ++cnt1;
    	for(auto v : V1[now]) dfs1(v), siz1[now] += siz1[v];
    }
    
    struct Tree
    {
    	int ls, rs, sum;
    }t[maxt];
    int root[maxn], tcnt = 0;
    In void insert(int old, int& now, int l, int r, int x)
    {
    	t[now = ++tcnt] = t[old];
    	t[now].sum++;
    	if(l == r) return;
    	int mid = (l + r) >> 1;
    	if(x <= mid) insert(t[old].ls, t[now].ls, l, mid, x);
    	else insert(t[old].rs, t[now].rs, mid + 1, r, x);
    }
    In int query(int old, int now, int l, int r, int L, int R)
    {
    	if(!old && !now) return 0;
    	if(l == L && r == R) return t[now].sum - t[old].sum;
    	int mid = (l + r) >> 1;
    	if(R <= mid) return query(t[old].ls, t[now].ls, l, mid, L, R);
    	else if(L > mid) return query(t[old].rs, t[now].rs, mid + 1, r, L, R);
    	else return query(t[old].ls, t[now].ls, l, mid, L, mid) + query(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R);
    }
    
    int siz2[maxn], dfn2[maxn], cnt2 = 0;
    In void dfs2(int now)
    {
    	siz2[now] = 1, dfn2[now] = ++cnt2;
    	if(now) insert(root[cnt2 - 1], root[cnt2], 1, n + 1, dfn1[n - now]);
    	for(auto v : V2[now]) dfs2(v), siz2[now] += siz2[v];
    }
    
    In void init()
    {
    	for(int i = 0; i <= n; ++i) V1[i].clear(), V2[i].clear();
    	cnt1 = cnt2 = tcnt = 0;
    	Mem(root, 0);
    }
    
    int main()
    {
    	int T = read();
    	while(T--)
    	{
    		n = read(), m = read();
    		init();
    		scanf("%s", s + 1);
    		kmp_init();
    		dfs1(0), dfs2(0);
    		for(int i = 1; i <= m; ++i)
    		{
    			int x = read(), y = read();
    			write(query(root[dfn2[y] - 1], root[dfn2[y] + siz2[y] - 1], 1, n + 1, dfn1[x], dfn1[x] + siz1[x] - 1)), enter;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    题解 LNOI2014 LCA
    题解 P3413 【SAC#1
    题解 P3372 【【模板】线段树 1】(珂朵莉树解法)
    题解 P2610 【[ZJOI2012]旅游】
    题解 CF911D 【Inversion Counting】
    题解 CF1037D 【Valid BFS?】
    bootstrap常用部件下载
    sql获取上月同期
    VSS配置
    SQL中的union,except,intersect用法
  • 原文地址:https://www.cnblogs.com/mrclr/p/15213464.html
Copyright © 2020-2023  润新知