题目大意:
中文题面, 就是在解密串之后询问第x行的串在第y行的串中出现了几次
串的最大长度 <= 10W, 询问数 <= 10W
大致思路:
首先不难将给出的串解出来插入到Trie树中, 那么对于每一次询问, 其实就是在对于所有串建立AC自动机之后, 考虑AC自动机的fail树, 第x行的字符串的结尾位置的结点是u的话, 就相当于问在fail树中, 以u为根的子树下有多少个结点是AC自动机上第y行的字符串经过的点
于是考虑到离线算法, 首先将所有的询问按照y相同, 这里理解为y对应的AC自动机上的结点相同, 将这些询问分在一组
然后对于fail树的时间戳用树状数组维护
从根节点开始dfs, 每次经过一个点就将这个点对应的时间戳上的位置+1, 离开这个点就-1, 这样的话dfs序在到达y的结点的时候, +1的区间位置一定是y串所经过的点, 那么就相当于标记了这些点是1其他的都是0, 于是用树状数组询问以x为根的子树下当前被标记的点的个数就是其时间戳对应的区间和了, 于是离线的做法时间复杂度是O(n + mlogn)
代码如下:
Result : Accepted Memory : 31312 KB Time : 720 ms
/* * Author: Gatevin * Created Time: 2015/10/21 15:06:29 * File Name: Sakura_Chiyo.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 100010 char in[maxn]; struct Ask { int x, y, id; Ask(){} Ask(int _x, int _y, int _id) : x(_x), y(_y), id(_id){} }; Ask ask[100010]; int pos[100010]; int result[100010]; vector<Ask> Query[maxn]; struct BIT//单点修改, 区间查询 { int C[maxn]; int N; int lowbit(int x) { return -x & x; } void add(int x, int val) { while(x <= N) C[x] += val, x += lowbit(x); } int sum(int x) { int ret = 0; while(x) ret += C[x], x -= lowbit(x); return ret; } void init() { memset(C, 0, sizeof(C)); } BIT(){} }; BIT bit; struct Trie { int next[maxn][26], fail[maxn], par[maxn];//par代表每个节点在Trie树上的父亲节点 int trie_next[maxn][26]; //建立AC自动机之后next数组会改变, 为了还原将建立AC自动机前原本的Trie储存在trie_next中 int L, root; vector<int> G[maxn];//fail 树 int newnode() { for(int i = 0; i < 26; i++) next[L][i] = -1; L++; return L - 1; } void init() { L = 0; root = newnode(); par[root] = root; } int insert(int now, deque<char> &Q, int cnt) { while(!Q.empty()) { int go = Q.front() - 'a'; Q.pop_front(); if(next[now][go] == -1) { next[now][go] = newnode(); par[next[now][go]] = now; } now = next[now][go]; } pos[cnt] = now;//记录每一个被询问的字符串的结尾位置编号 return now; } void construct(int len) { deque<char> Q; int now = root; int cnt = 1; int i = 0; while(i < len) { if(!Q.empty()) Q.clear(); while(i < len && in[i] != 'P') { if(in[i] == 'B') { if(Q.empty()) now = par[now]; else Q.pop_back(); } else Q.push_back(in[i]); i++; } if(in[i] == 'P') { i++; now = insert(now, Q, cnt++); } } return; } void save()//保留住原来的trie { for(int i = 0; i < L; i++) for(int j = 0; j < 26; j++) trie_next[i][j] = next[i][j]; } void build()//建立AC自动机 { fail[root] = root; queue<int> Q; Q.push(root); while(!Q.empty()) { int now = Q.front(); Q.pop(); for(int i = 0; i < 26; i++) { if(next[now][i] == -1) next[now][i] = now == root ? root : next[fail[now]][i]; else { fail[next[now][i]] = now == root ? root : next[fail[now]][i]; Q.push(next[now][i]); } } } return; } int tim, left[maxn], right[maxn];//时间戳用 void dfs(int now) { left[now] = ++tim; for(int i = 0, sz = G[now].size(); i < sz; i++) dfs(G[now][i]); right[now] = tim; } void answer(int now) { bit.add(left[now], 1); if(!Query[now].empty()) for(int i = 0, sz = Query[now].size(); i < sz; i++) { if(pos[Query[now][i].x] == root || pos[Query[now][i].y] == root) result[Query[now][i].id] = 0; result[Query[now][i].id] = bit.sum(right[pos[Query[now][i].x]]) - bit.sum(left[pos[Query[now][i].x]] - 1); } for(int i = 0; i < 26; i++) if(trie_next[now][i] != -1) answer(trie_next[now][i]); bit.add(left[now], -1); } void solve() { init(); memset(pos, 0, sizeof(pos)); for(int i = 0; i < maxn; i++) Query[i].clear(), G[i].clear(); gets(in); int len = strlen(in); construct(len); int m; scanf("%d", &m); for(int i = 0; i < m; i++) { scanf("%d %d", &ask[i].x, &ask[i].y); ask[i].id = i; Query[pos[ask[i].y]].push_back(ask[i]); } save(); build(); for(int i = 1; i < L; i++)//找出fail树 G[fail[i]].push_back(i); tim = 0; dfs(root);//建立fail树的时间戳区间 bit.N = tim + 1; bit.init();//初始化树状数组 answer(root);//遍历自动机利用树状数组完成询问 for(int i = 0; i < m; i++) printf("%d\n", result[i]); } }; Trie ac; int main() { ac.solve(); return 0; } /* aPaPBbP 3 1 2 1 3 2 3 */