日常吐槽:哈哈哈哈哈哈哈我终于改完这个板了啊……竟然和主席树一样又是四天...不过还做了比赛,所以还是挺不错的。目前要学的字符串算法好像只差后缀自动机了,嗯,接下来要备战NOIP了呢。
不过为什么暑假只有30天???八月还要集训???好像作业还没动???
思路
其实就是在一颗Trie树上把多个字符串用类似于next数组的fail指针连成一个大的KMP。总而言之就是让被匹配的那一长串不停地往前匹配而不退回,然后就跳来跳去的咯,原理是和KMP一样的。更好地理解fail指针和如何实现移步https://blog.csdn.net/u013371163/article/details/60469145,不过博主写得实在是太可爱了=v=。
例题
JZOJ1566 单词查找
代码
造福人类的时间到了!网上的模板题50个有49个都!是!用!指!针!实!现!作为一名不用指针的忠实党已经痛苦得不想说话。感谢师兄的模板,救我于水火之中*v*
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int len, leng, n, tot = 0, ans = 0; int w[30001], son[30001][26], fail[30001]; char s[2000001]; void Trie_build() { int now = 0; for (int i = 0; i < len; i++) { int p = s[i] - 'a'; if (!son[now][p]) son[now][p] = ++tot; now = son[now][p]; } w[now]++; } void AC_build() { int q[30001], h = 0, t = 1; q[t] = 0; while (h != t) { h++; int head = q[h]; for (int i = 0; i < 26; i++) { if (son[head][i]) { q[++t] = son[head][i]; if (!head) { fail[son[head][i]] = 0; continue; } int k = fail[head]; while (k && !son[k][i]) k = fail[k]; fail[son[head][i]] = son[k][i]; w[son[head][i]] += w[son[k][i]]; } } } } void AC_work() { int head = 0; for (int i = 0; i < leng; i++) { int p = s[i] - 'a'; if (son[head][p]) head = son[head][p]; else { int k = fail[head]; while (k && !son[k][p]) k = fail[k]; if (!son[k][p]) head = 0; else head = son[k][p]; } ans += w[head]; } } int main() { memset(w, 0, sizeof w); memset(son, 0, sizeof son); memset(fail, 0, sizeof fail); scanf("%d%d", &leng, &n); for (int i = 1; i <= n; i++) { scanf("%s", s); len = strlen(s); Trie_build(); } scanf("%s", s); AC_build(); AC_work(); printf("%d\n", ans); return 0; }