#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct triesNode {
int children[26];
int fail;
int end;
int sum;
inline void reset() {
for (int i = 0; i < 26; ++i) {
children[i] = 0;
}
fail = end = sum = 0;
}
void addEnd() {
++end;
}
void addChildren(int index, int nIndex) {
children[index] = nIndex;
}
};
const int N = 601000;
char s[N];
// 下标分配
// 注意越界问题
template<size_t S>
struct idAllocator {
// 存储回收的节点的 id
int recycledQueue[S];
// [0, recycleTop] 可回收区域
// 用户回收再利用
int recycledIndex;
// 最大下一个可用下标, 只增不减
int currentMaxAllocID;
idAllocator() {
recycledIndex = 0;
currentMaxAllocID = 0;
}
// 若回收队列不空, 从回收队列取, 否则重新申请
int get() {
if (recycledIndex > 0) {
return recycledQueue[recycledIndex--];
}
return ++currentMaxAllocID;
}
void put(int x) {
recycledQueue[++recycledIndex] = x;
}
};
// 对象池
// 注意越界问题
template<size_t S, typename Object, typename IdAllocator = idAllocator<S>>
struct ObjectPool {
IdAllocator idAlloc;
Object freeNode[S];
// 通过分配下标来简化
inline int newNode() {
return idAlloc.get();
}
Object &getObject(int x) {
return freeNode[x];
}
void put(int x) {
idAlloc.put(x);
freeNode[x].reset();
}
};
ObjectPool<N, triesNode> pool;
int myQueue[N];
// 简化pool的使用函数
void allocForChild(int nIndex, int cIndex) {
pool.freeNode[nIndex].addChildren(cIndex, pool.newNode());
}
int &getChildIndex(int nIndex, int cIndex) {
return pool.getObject(nIndex).children[cIndex];
}
triesNode& getObject(int nIndex) {
return pool.getObject(nIndex);
}
struct AcAuto {
int rootIndex;
void reset() {
// 必须保证 每次从 pool 取出来的对象都是初始化好的
rootIndex = pool.newNode();
}
void add(const char *str) {
int nIndex = rootIndex;
for (int i = 0; str[i] != '\0'; i++) {
int cIndex = str[i] - 'a';
if (!getChildIndex(nIndex, cIndex)) {
allocForChild(nIndex, cIndex);
}
nIndex = getChildIndex(nIndex, cIndex);
}
getObject(nIndex).addEnd();
}
void build_fail() {
int current = 1;
int top = 1;
myQueue[current] = rootIndex;
while (current <= top) {
int nIndex = myQueue[current++];
for (int i = 0; i < 26; i++) {
int cIndex = getChildIndex(nIndex, i);
int fIndex = getObject(nIndex).fail;
if (!cIndex) {
continue;
}
myQueue[++top] = cIndex;
// 找 fail
while (fIndex && !getChildIndex(fIndex, i)) {
fIndex = getObject(fIndex).fail;
}
if (fIndex && getChildIndex(fIndex, i) != cIndex) {
getObject(cIndex).fail = getChildIndex(fIndex, i);
} else {
getObject(cIndex).fail = rootIndex;
}
getObject(cIndex).sum = getObject(cIndex).end + getObject(getObject(cIndex).fail).sum;
};
}
}
ll scan(const char *str) {
int nIndex = rootIndex;
ll res = 0;
for (int i = 0; str[i] != '\0'; i++) {
int x = str[i] - 'a';
while (nIndex != rootIndex && !getChildIndex(nIndex, x)) {
nIndex = getObject(nIndex).fail;
}
if (getChildIndex(nIndex, x)) {
nIndex = getChildIndex(nIndex, x);
}
res += getObject(nIndex).sum;
}
return res;
}
// 二项合并
void merge(int &x, int y) {
if (!y) {
return;
}
if (!x) {
x = pool.newNode();
}
for (int i = 0; i < 26; i++) {
merge(getChildIndex(x, i), getChildIndex(y, i));
}
getObject(x).end += getObject(y).end;
pool.put(y);
}
};
const int treeNum = 20;
struct Tries {
int top, num[treeNum];
AcAuto ac[treeNum];
Tries() {
top = 0;
memset(num, 0, sizeof(num));
}
void add(const char *str) {
num[++top] = 1;
ac[top].reset();
ac[top].add(str);
while (top > 1) {
if (num[top] == num[top - 1]) {
ac[top - 1].merge(ac[top - 1].rootIndex, ac[top].rootIndex);
num[top - 1] += num[top];
num[top] = 0;
--top;
} else {
break;
}
}
ac[top].build_fail();
}
ll scan(char *str) {
ll res = 0;
for (int i = 1; i <= top; i++) {
res += ac[i].scan(str);
}
return res;
}
};
int main() {
int n;
cin >> n;
Tries treeAdd;
Tries trieSub;
while (n--) {
int tp;
cin >> tp;
scanf("%s", s);
if (tp == 1) {
treeAdd.add(s);
continue;
}
if (tp == 2) {
trieSub.add(s);
continue;
}
ll times = treeAdd.scan(s) - trieSub.scan(s);
printf("%d\n", times);
fflush(stdout);
}
}
c++ ac 自动机的实现
最新推荐文章于 2024-06-04 08:36:20 发布