CodeForces 710F 强制在线AC自动机
题目链接:http://codeforces.com/contest/710/problem/F
题意:维护一个集合,集合要求满足三种操作。 1 str:向集合插入字符串str(保证不会插入之前已经插入过的字符串) 2str:从集合中删除字符串str(保证删除的str一定在集合中) 3 str:str的子串有多少个在集合中出现过。
思路:题目意思就是一个可以插入/删除/查询的AC自动机。但是如果我们暴力求解,每次添加/删除一个字符串到自动机中求从前求一边适配指针的话会TLE。所以我们考虑用其他方法,设置一个恰当的阈值。如果小于这个阈值则把字符串放入trie树中,而大于阈值的直接保存下来。然后对于添加标记+1 ,删除标记-1。关于操作3把字符串分别与字典树的串匹配和保存下来的串匹配。关于在字典树的匹配。我们只需枚举str的所有后缀去匹配字典树即可。对于保存下来的串我们对其每一个建立KMP的next数组,然后就是普通的KMP的单模式匹配了。 用时不到1S
#define _CRT_SECURE_NO_DEPRECATE #include<stdio.h> #include<string.h> #include<cstring> #include<algorithm> #include<queue> #include<math.h> #include<time.h> #include<vector> #include<iostream> using namespace std; typedef long long int LL; const int MAXN = 300000 + 10; const int CASE_SIZE = 26; const int S_SIZE = 1000; const int B_SIZE = MAXN / S_SIZE + 10; struct Trie{ int child[MAXN][CASE_SIZE], value[MAXN],trieN,root; void init(){ trieN = root = 0; value[root] = 0; memset(child[root], -1, sizeof(child[root])); } int newnode(){ trieN++; value[trieN] = 0; memset(child[trieN], -1, sizeof(child[trieN])); return trieN; } void insert(char *s,int val){ int x = root; for (int i = 0; s[i]; i++){ int d = s[i] - 'a'; if (child[x][d] == -1){ child[x][d] = newnode(); } x = child[x][d]; } value[x] += val; } int search(char *s){ int sum = 0, x = root; for (int i = 0; s[i]; i++){ int d = s[i] - 'a'; if (child[x][d] == -1){ break; } x = child[x][d]; sum += value[x]; } return sum; } }trie; struct KMP{ int Next[MAXN]; void getNext(string s,int len){ memset(Next, 0, sizeof(Next)); int i = 0, j = -1; Next[0] = -1; while (i < len){ if (j == -1 || s[i] == s[j]){ ++i; ++j; Next[i] = j; } else{ j = Next[j]; } } } int search(string s,char *str){ int sum = 0, j = 0, lens = s.length(),lenstr=strlen(str); getNext(s, s.length()); for (int i = 0; i < lenstr; i++){ while (j>0 && s[j] != str[i]){ j = Next[j]; } if (s[j] == str[i]){ j++; } if (j == lens){ sum++; j = Next[j]; } } return sum; } }kmp; string Bstr[B_SIZE]; char str[MAXN]; int cnt, Bvalue[B_SIZE]; int main() { int t, m,len,val; while (~scanf("%d", &m)) { trie.init(); cnt = 0; memset(Bvalue, 0, sizeof(Bvalue)); for (int i = 1; i <= m; i++){ scanf("%d%s", &t, str); len = strlen(str); if (t == 1 || t == 2){ //插入和删除 val = (t == 1 ? 1 : -1); //插入=1,删除=-1 if (len <= S_SIZE){//小与阈值删除字典树 trie.insert(str, val); } else{ //大于阈值直接保存起来 Bstr[cnt] = string(str); Bvalue[cnt++] = val; //标记是添加的还是删除的 } } else{ //查询 LL ans = 0; for (int i = 0; i < len; i++){ //字典树上匹配 ans += trie.search(str + i); } for (int i = 0; i < cnt; i++){ //暴力KMP匹配 if (Bstr[i].length() > len){ continue; } ans += kmp.search(Bstr[i], str)*Bvalue[i]; } printf("%I64d\n", ans); fflush(stdout); } } } return 0; }