AC自动机(无指针)
哈哈哈哈哈哈哈我终于改完这个板了啊……目前要学的字符串算法好像差不多了。
思路
其实就是在一颗Trie树上把多个字符串用类似于next数组的fail指针连成一个大的KMP。 总而言之就是让被匹配的那一长串不停地往前匹配而不退回,然后就跳来跳去的咯,原理是和KMP一样的。更好地理解fail指针和如何实现移步https://blog.csdn.net/u013371163/article/details/60469145,不过博主写得实在是太可爱了=v=。
JZOJ1566 单词查找
Description
给一个主串S以及M个字符串。问这M个字符串在主串中出现多少次。
Input
第一行一个整数N,表示S的长度。
给一个主串S以及M个字符串。问这M个字符串在主串中出现多少次。
下面M行,每行一个字符串,定义如题。
下面一行N个字母,即主串S。
Output
一个正整数,表示出现的次数。
Sample Input
10
3
a
aba
abc
abcabababa
Sample Output
9
Hint
【数据范围】
对于40%的数据,1<=n<=10000,1<=m<=100
对于100%的数据,1<=n<=2000000,1<=m<=3000,1<=l<=10
保证所有字母都是英文小写字母。
代码
#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;
}