• Luogu2414 [NOI2011]阿狸的打字机


    题目蓝链

    Description

    打字机上只有28个按键,分别印有26个小写英文字母和BP两个字母。经阿狸研究发现,这个打字机是这样工作的:

    • 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)
    • 按一下印有B的按键,打字机凹槽中最后一个字母会消失
    • 按一下印有P的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失

    例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

    a aa ab我们把纸上打印出来的字符串从(1)开始顺序编号,一直到(n)。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数((x,y))(其中(1 leq x, y leq n)),打字机会显示第(x)个打印的字符串在第(y​)个打印的字符串中出现了多少次。

    阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

    Solution

    我们首先对于所有的串构建出它们的AC自动机,我们发现问题转换为了求(y)串到根节点的路径中有多少个节点处于(fail)树中(x)串结尾节点的子树中(相当于枚举(y)串的每一个前缀,判断(x)是不是这个前缀的后缀)

    我们对于(fail)树与处理出每一个节点的(dfn)以及(size),用一个(BIT)去维护这个(dfn)序列。然后我们对字典树进行一遍(dfs),每到一个节点的时候在(BIT)里对于这一个节点加(1),离开的时候再减掉。这样我们在处理(y)串为当前串的询问时,我们就直接对(x)串末尾节点所在的子树求和就可以了

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define fst first
    #define snd second
    #define mp make_pair
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
    template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    const int maxn = 1e5 + 10;
    
    int n, m, p[maxn], ans[maxn];
    char S[maxn];
    
    vector<pii> Q[maxn];
    
    namespace ACAM {
    
    	int cnt, id, Index;
    	int fa[maxn], son[maxn][26], fail[maxn], dfn[maxn], sz[maxn];
    
    	vector<int> B[maxn], g[maxn];
    
    	inline void init(char *s) {
    		cnt = id = Index = 0;
    		int now = 0; ++cnt;
    		while (*s) {
    			int c = *s - 'a';
    			if (*s == 'B') now = fa[now];
    			else if (*s == 'P') p[++id] = now, B[now].push_back(id);
    			else {
    				if (!son[now][c]) son[now][c] = ++cnt, fa[cnt] = now;
    				now = son[now][c];
    			}
    			++s;
    		}
    	}
    
    	inline void build() {
    		static int tmp[maxn][26];
    		memcpy(tmp, son, sizeof tmp);
    		queue<int> q;
    		for (int i = 0; i < 26; i++)
    			if (son[0][i]) q.push(son[0][i]), fail[son[0][i]] = 0, g[0].push_back(son[0][i]);
    		while (!q.empty()) {
    			int now = q.front(); q.pop();
    			for (int i = 0; i < 26; i++)
    				if (son[now][i])
    					fail[son[now][i]] = son[fail[now]][i], g[son[fail[now]][i]].push_back(son[now][i]), q.push(son[now][i]);
    				else son[now][i] = son[fail[now]][i];
    		}
    		memcpy(son, tmp, sizeof son);
    	}
    
    	namespace BIT {
    #define lowbit(x) ((x) & (-(x)))
    		int A[maxn];
    		inline void change(int x, int v) { for (int i = x; i <= cnt; i += lowbit(i)) A[i] += v; }
    		inline int sum(int x) { int res = 0; for (int i = x; i >= 1; i -= lowbit(i)) res += A[i]; return res; }
    		inline int query(int x, int y) { return sum(y) - sum(x - 1); }
    #undef lowbit
    	}
    
    	inline void dfs_fail(int x) {
    		dfn[x] = ++Index, sz[x] = 1;
    		for (int y : g[x]) dfs_fail(y), sz[x] += sz[y];
    	}
    
    	inline void dfs(int x) {
    		BIT::change(dfn[x], 1);
    		for (pii y : Q[x]) ans[y.snd] = BIT::query(dfn[y.fst], dfn[y.fst] + sz[y.fst] - 1);
    		for (int i = 0; i < 26; i++)
    			if (son[x][i]) dfs(son[x][i]);
    		BIT::change(dfn[x], -1);
    	}
    }
    
    int main() {
    #ifdef xunzhen
    	freopen("print.in", "r", stdin);
    	freopen("print.out", "w", stdout);
    #endif
    
    	static char S[maxn];
    	scanf("%s", S), n = strlen(S);
    	ACAM::init(S), ACAM::build(), ACAM::dfs_fail(0);
    
    	m = read();
    	for (int i = 1; i <= m; i++) {
    		int x = p[read()], y = p[read()];
    		Q[y].push_back(mp(x, i));
    	}
    
    	ACAM::dfs(0);
    
    	for (int i = 1; i <= m; i++) printf("%d
    ", ans[i]);
    
    	return 0;
    }
    

    Summary

    最近思维僵化得有点严重,我一直在想怎么用(y)串去查询,没想到其实可以反过来用(x)串去查询

    总的来说,这是一道深入理解AC自动机的好题(本人太菜求大佬轻喷)

  • 相关阅读:
    JVM全面分析之程序计时器
    JVM全面分析之类加载系统
    问题
    -----------------------算法学习篇:斐波拉契数列------------------------
    -----------------------------------A Tour of C++ Chapter8-------------------------------------------
    -----------------Clean Code《代码整洁之道》--------------------
    -----------------------------------A Tour of C++-------------------------------------------
    -----------------Clean Code《代码整洁之道》--------------------
    http响应状态码大全
    DOM之通俗易懂讲解
  • 原文地址:https://www.cnblogs.com/xunzhen/p/10633184.html
Copyright © 2020-2023  润新知