居然真的遇上了这种蔡队题。瑟瑟发抖。
题目背景
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。
题目描述
打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。经阿狸研究发现,这个打字机是这样工作的:
·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
·按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
·按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a aa ab 我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
输入输出格式
输入格式:
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
输出格式:
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
输入输出样例
说明
数据范围:
对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000。
题意:
给n个字符串问每次有m个询问,问第x个字符串在第y个字符串中出现了几次。
思路:
最开始是暴力建树,在字符串的结尾标记序号,然后每次询问暴力在树上查找。40分TLE
后来去看题解。题解看了无敌久。
实际上我们可以发现我们每次都是沿着AC自动机的fail指针在跳,在y中匹配x就是在属于y字符串的节点中能找到多少个节点可以跳到x的结束节点。
那么我们可以把原本的边都删去只保留fail指针,这就是AC自动机上的fail树。
把fail指针的方向反过来,其实就是找以x为根的树中有多少个节点是属于y的。
但是查询有很多组,暴力查询还是不可以的。由于每次查询我们需要去标记y节点,查询完了y节点的标记要删去。所以可以考虑先把所有查询排个序,把所有y相同的放在一起进行查询。并且我们处理出查询中某个x的下标区间,当dfs过程中访问到了这个x的结束节点就同时更新这些查询的答案。
建树的过程中先记下对应序号的字符串的结束节点下标和父亲节点,遇到“B”我们就回退到父亲节点,遇到“P”就打结束标记。
然后获取fail指针,并用fail指针建边,跑一遍dfs序,得到以x为根的子树的区间。
对排好序的问题,我们预处理出所有以y为第二个参数的查询的编号区间。
用树状数组维护子树和,dfs查询子树和,当访问到y的结尾时,说明这一组y完成了打标记工作,可以去求子树和了。那么就更新一下这一组查询的答案。然后要记得将这段子树的值-1
最后重新将答案映射会原来的顺序输出。
暴力40分代码:
1 #include <iostream> 2 #include <set> 3 #include <cmath> 4 #include <stdio.h> 5 #include <cstring> 6 #include <algorithm> 7 #include <vector> 8 #include <queue> 9 #include <map> 10 using namespace std; 11 typedef long long LL; 12 #define inf 0x7f7f7f7f 13 14 int n, m; 15 const int maxn = 1e5 + 5; 16 17 struct Tree{ 18 int fail;//失配指针 19 int vis[26];//子节点位置 20 int ed = -1; 21 }AC[maxn]; 22 string s[maxn]; 23 int tot = 0, cnt = 0; 24 25 void build(string sss, int id) 26 { 27 //cout<<sss<<endl; 28 int len = sss.length(); 29 int now = 0;//字典树当前指针 30 for(int i = 0; i < len; i++){ 31 if(AC[now].vis[sss[i] - 'a'] == 0){ 32 AC[now].vis[sss[i] - 'a'] = ++tot; 33 } 34 now = AC[now].vis[sss[i] - 'a']; 35 } 36 AC[now].ed = id; 37 } 38 39 void get_fail() 40 { 41 queue<int> que; 42 for(int i = 0; i < 26; i++){ 43 if(AC[0].vis[i] != 0){ 44 AC[AC[0].vis[i]].fail = 0; 45 que.push(AC[0].vis[i]); 46 } 47 } 48 while(!que.empty()){ 49 int u = que.front(); 50 que.pop(); 51 for(int i = 0; i < 26; i++){ 52 if(AC[u].vis[i] != 0){ 53 AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i]; 54 que.push(AC[u].vis[i]); 55 } 56 else{ 57 AC[u].vis[i] = AC[AC[u].fail].vis[i]; 58 } 59 } 60 } 61 } 62 63 int AC_query(int x, int y) 64 { 65 int leny = s[y].length(); 66 int now = 0, ans = 0; 67 for(int i = 0; i < leny; i++){ 68 now = AC[now].vis[s[y][i] - 'a']; 69 for(int t = now; t; t = AC[t].fail){ 70 if(AC[t].ed == x){ 71 ans++; 72 } 73 } 74 } 75 return ans; 76 } 77 78 int main() 79 { 80 string str; 81 char tmp[maxn]; 82 cin>>str; 83 int len = str.length(), j = 0; 84 for(int i = 0; i < len; i++){ 85 if(str[i] == 'B'){ 86 tmp[j] = 0; 87 j--; 88 } 89 else if(str[i] == 'P'){ 90 91 s[cnt] = tmp; 92 //cout<<s[cnt]<<endl; 93 build(s[cnt], cnt);cnt++; 94 } 95 else{ 96 tmp[j++] = str[i]; 97 } 98 } 99 AC[0].fail = 0; 100 get_fail(); 101 scanf("%d", &m); 102 while(m--){ 103 int x, y; 104 scanf("%d %d", &x, &y); 105 //cout<<s[x - 1]<<" "<<s[y - 1]<<endl; 106 cout<<AC_query(x - 1, y - 1)<<endl; 107 } 108 //cout<<s[maxid]<<endl; 109 //cout<<AC_query(s)<<endl; 110 return 0; 111 }
AC100分代码:
1 #include <iostream> 2 #include <set> 3 #include <cmath> 4 #include <stdio.h> 5 #include <cstring> 6 #include <algorithm> 7 #include <vector> 8 #include <queue> 9 #include <map> 10 using namespace std; 11 typedef long long LL; 12 #define inf 0x7f7f7f7f 13 14 int n, m; 15 const int maxn = 1e5 + 5; 16 17 char ss[maxn]; 18 int nd[maxn], tot; 19 int ans[maxn]; 20 int tree[maxn]; 21 int dfn[maxn], low[maxn], tim; 22 int ql[maxn], qr[maxn];//dfs序左端点和右端点 23 struct Tree{ 24 int fail, fa; 25 int son[26];//子节点位置 26 int vis[26]; 27 int ed = -1; 28 }AC[maxn]; 29 struct query{ 30 int x, y, id, ans; 31 }q[maxn]; 32 bool operator < (query a, query b){return a.y < b.y;} 33 void update(int x, int k) 34 { 35 while(x <= tim){ 36 tree[x] += k; 37 x += x & (-x); 38 } 39 } 40 41 int sum(int x) 42 { 43 int ans = 0; 44 while(x){ 45 ans += tree[x]; 46 x -= x & (-x); 47 } 48 return ans; 49 } 50 51 /*void build(string sss, int rt) 52 { 53 //cout<<sss<<endl; 54 int len = sss.length(); 55 int now = 0;//字典树当前指针 56 for(int i = 0; i < len; i++){ 57 if(AC[now].vis[sss[i] - 'a'] == 0){ 58 AC[now].vis[sss[i] - 'a'] = ++tot; 59 } 60 now = AC[now].vis[sss[i] - 'a']; 61 } 62 AC[now].ed = id; 63 }*/ 64 65 void get_fail() 66 { 67 queue<int> que; 68 for(int i = 0; i < 26; i++){ 69 if(AC[0].son[i] != 0){ 70 //AC[AC[0].vis[i]].fail = 0; 71 que.push(AC[0].son[i]); 72 } 73 } 74 while(!que.empty()){ 75 int u = que.front(); 76 que.pop(); 77 for(int i = 0; i < 26; i++){ 78 if(AC[u].son[i] != 0){ 79 AC[AC[u].son[i]].fail = AC[AC[u].fail].son[i]; 80 que.push(AC[u].son[i]); 81 } 82 else{ 83 AC[u].son[i] = AC[AC[u].fail].son[i]; 84 } 85 } 86 } 87 } 88 89 struct edge{ 90 int v, nxt; 91 }e[maxn << 1]; 92 int head[maxn], cnt = 1; 93 void addedge(int u, int v) 94 { 95 e[cnt] = (edge){v, head[u]}; 96 head[u] = cnt++; 97 } 98 99 void dfs_sort(int rt) 100 { 101 dfn[rt] = ++tim; 102 for(int i = head[rt]; i; i = e[i].nxt)dfs_sort(e[i].v); 103 low[rt] = tim; 104 } 105 106 void dfs(int rt) 107 { 108 update(dfn[rt], 1); 109 if(AC[rt].ed){ 110 for(int i = ql[AC[rt].ed]; i <= qr[AC[rt].ed]; i++){ 111 q[i].ans = sum(low[nd[q[i].x]]) - sum(dfn[nd[q[i].x]] - 1); 112 } 113 } 114 for(int i = 0; i < 26; i++){ 115 if(AC[rt].vis[i]){ 116 dfs(AC[rt].vis[i]); 117 } 118 } 119 update(dfn[rt], -1); 120 } 121 122 int main() 123 { 124 scanf("%s", ss + 1); 125 int now = 0; 126 int len = strlen(ss + 1); 127 for(int i = 1; i <= len; i++){ 128 if(ss[i] == 'B'){ 129 now = AC[now].fa; 130 } 131 else if(ss[i] == 'P'){ 132 nd[++n] = now; 133 AC[now].ed = n; 134 } 135 else{ 136 if(!AC[now].son[ss[i] - 'a']){ 137 AC[now].son[ss[i] - 'a']= ++tot; 138 AC[tot].fa = now; 139 now = AC[now].son[ss[i] - 'a']; 140 } 141 } 142 } 143 for(int i = 0; i <= tot; i++){ 144 for(int j = 0; j < 26; j++){ 145 AC[i].vis[j] = AC[i].son[j]; 146 } 147 } 148 //AC[0].fail = 0; 149 get_fail(); 150 for(int i = 1; i <= tot; i++){ 151 addedge(AC[i].fail, i); 152 } 153 dfs_sort(0); 154 scanf("%d", &m); 155 for(int i = 1; i <= m; i++){ 156 scanf("%d %d", &q[i].x, &q[i].y); 157 q[i].id = i; 158 //cout<<s[x - 1]<<" "<<s[y - 1]<<endl; 159 } 160 sort(&q[1], &q[m + 1]); 161 for(int i = 1, pos = 1; i <= m; i = pos){ 162 ql[q[i].y] = i; 163 while(q[pos].y == q[i].y) pos++; 164 qr[q[i].y] = pos - 1; 165 } 166 dfs(0); 167 for(int i = 1; i <= m; i++){ 168 ans[q[i].id] = q[i].ans; 169 } 170 for(int i = 1; i <= m; i++){ 171 printf("%d ", ans[i]); 172 } 173 //cout<<s[maxid]<<endl; 174 //cout<<AC_query(s)<<endl; 175 return 0; 176 }