【NOI 2011】阿狸的打字机(AC自动机+fail树)
题目背景
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。
题目描述
打字机上只有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个询问的答案。
输入输出样例
输入 #1
aPaPBbP
3 1 2 1 3 2 3
输出 #1
2
1 0
说明/提示
数据范围:
对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000
【题目大意】
给定一串字符串,其中包含一些特殊字符,代表一些操作,操作如下
1:B 消去凹槽中最后一个字符
2:P 打印出凹槽中所有字符
知识点:
1.AC自动机
2.对fail树的操作
注意:
理解fail树的本质,因为没有弄清楚查了好久。
题解咕了
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<string> 6 #include<vector> 7 #include<queue> 8 using namespace std; 9 const int MAXN = 100010; 10 int cnt, siz = 0; 11 int exist[MAXN], last[MAXN]; 12 int fail[MAXN], tire[MAXN][27]; 13 vector<int> fail_tree[MAXN]; 14 int ans_arr[MAXN]; 15 int in[2 * MAXN], out[2 * MAXN], tree[2 * MAXN]; 16 int lowbit(int n) 17 { 18 return (n&(-n)); 19 } 20 int book[MAXN]; 21 int st[MAXN]; 22 int top = 0; 23 int size_[MAXN]; 24 struct note 25 { 26 int pos; 27 int x; 28 //NOte(int a,int b):x(a),pos(b){} 29 }; 30 vector<note> ask[MAXN]; 31 32 void insert(string s) 33 { 34 int u = 0; 35 for (char c : s) 36 { 37 if (c == 'B') 38 { 39 u = fai[u]; 40 } 41 else 42 { 43 if (c == 'P') 44 { 45 siz++; 46 last[siz] = u; 47 } 48 else 49 { 50 int i = c - 'a'; 51 if (tire[u][i] == 0) 52 { 53 cnt++; 54 tire[u][i] = cnt; 55 fail[tire[u][i]] = u; 56 } 57 u = tire[u][i]; 58 } 59 } 60 } 61 return; 62 } 63 void Get_Fail() 64 { 65 queue<int> que; 66 for (int i = 0; i < 26; i++) 67 { 68 if (tire[0][i]) 69 { 70 fail[tire[0][i]] = 0; 71 que.push(tire[0][i]); 72 fail_tree[0].push_back(tire[0][i]); 73 } 74 } 75 while (!que.empty()) 76 { 77 int u = que.front(); 78 que.pop(); 79 for (int i = 0; i < 26; i++) 80 { 81 int vis = tire[u][i]; 82 if (vis != 0) 83 { 84 fail[vis] = tire[fail[u]][i]; 85 que.push(vis); 86 fail_tree[fail[vis]].push_back(vis); 87 } 88 else 89 { 90 tire[u][i] = tire[fail[u]][i]; 91 } 92 } 93 } 94 95 return; 96 } 97 void Dfs(int num) 98 { 99 cnt++; 100 in[num] = cnt; 101 for (int t : fail_tree[num]) 102 { 103 Dfs(t); 104 } 105 out[num] = cnt; 106 return; 107 } 108 void update(int x, int v) 109 { 110 for (int i = x; i < MAXN; i += lowbit(i)) 111 { 112 tree[i] += v; 113 } 114 return; 115 } 116 int query(int x) 117 { 118 int ans = 0; 119 for (int i = x; i > 0; i -= lowbit(i)) 120 { 121 ans += tree[i]; 122 } 123 return ans; 124 } 125 int main() 126 { 127 int n; 128 string s; 129 memset(st, -1, sizeof(st)); 130 cin >> s; 131 //printf("**\n"); 132 insert(s); 133 //printf("**\n"); 134 Get_Fail(); 135 //printf("**\n"); 136 cnt = 0; 137 Dfs(0); 138 //printf("**\n"); 139 cin >> n; 140 for (int i = 1; i <= n; i++) 141 { 142 int x, y; 143 cin >> x >> y; 144 note tmp; 145 tmp.x = x, tmp.pos = i; 146 ask[y].push_back(tmp); 147 //printf("%d %d\n", ask[y][ask[y].size() - 1].x, ask[y][ask[y].size() - 1].pos); 148 } 149 int u = 0; 150 siz = 0; 151 //update(in[0], 1); 152 for (char c : s) 153 if (c == 'B') 154 { 155 update(in[u], -1); 156 u = fail[u]; 157 } 158 else 159 { 160 if (c == 'P') 161 { 162 siz++; 163 for (int i = 0; i < (int)ask[siz].size(); i++) 164 { 165 int x = last[ask[siz][i].x]; 166 ans_arr[ask[siz][i].pos] = query(out[x]) - query(in[x] - 1); 167 } 168 } 169 else 170 { 171 int i = c - 'a'; 172 u = tire[u][i]; 173 update(in[u], 1); 174 } 175 } 176 for (int i = 1; i <= n; i++) 177 { 178 printf("%d\n", ans_arr[i]); 179 } 180 return 0; 181 }