#includeusing namespace std; const int maxn = 1e6 + 5; const char Ch = 'a'; struct node { int next[30]; int idx, fail, cnt;//idx表示输入的第几个字符串的终止位置,fail是失配指针,cnt存该字符串出现的次数 void clear() { memset(next, 0, sizeof(next)); idx = 0, fail = -1, cnt = 0; } } t[maxn]; int tot;//指针. void init() { cout< q; for(int i = 0; i < 26; i ++) { //将树的第一层的所有节点的fail指针初始化为0即指向root节点. int pos = t[0].next[i]; if(pos) { t[pos].fail = 0; q.push(pos);//将每个节点的指针tot入队. } } while(q.size()) { int pos = q.front(); q.pop();//此时的pos为指向下一层的指针. for(int i = 0; i < 26; i ++) { int son = t[pos].next[i], fail_pos = t[pos].fail;//son为pos的子节点的指针 if(son) { //子节点存在,将子节点的指fail针指更新为父亲节点的fail指针 t[son].fail = t[fail_pos].next[i];//如果父节点的失配指针指向的节点fail_pos 他也有值是i的子节点,那么son的失配指针就可以指向那个子节点了. q.push(son);//将子节点入队 } else { t[pos].next[i] = t[fail_pos].next[i];//同个前缀不同字符指向相同节点,就是一路往回退,最后都会退回root节点的 } } } } int query(string s) { int len = s.length(), pos = 0, ans = 0, j; for(int i = 0; i < len; i ++) { pos = t[pos].next[s[i] - Ch]; j = pos; while(j && t[pos].cnt != -1) { //加过下次就不加了,标记一下 ans += t[pos].cnt; t[pos].cnt = -1; j = t[j].fail;//进入fail指针指向的节点 } } return ans; } void solve() { // init(); int n; cin >> n; for(int i = 1; i <= n; i ++) { //建树 . string a; cin >> a; insert(a); } getFail();//建立fail指针. string s; cin >> s; cout << query(s) << endl;//查询,并输出 } int main() { solve(); return 0; }
算法介绍可参考:AC自动机(算法介绍)_kilig_CSM的博客-CSDN博客https://blog.csdn.net/kilig_CSM/article/details/119341634



