题意:给出一个目标串,然后有n个询问,0 模式串 是询问模式串可重叠在目标串中出现了多少次,1 模式串 是询问模式串不可重叠在目标串中出现了多少次。
题解:可重叠的就是普通的模板题,不可重叠。不可重叠意味着每个匹配模式串的开头位置一定在上一个匹配结束位置之后,也就是 当前目标串位置 - 上一个匹配模式串的结束位置 >= 当前字符在模式串中的位置。trie树的val[i]数组就可以存i节点在模式串中的位置。然后再添加一个last数组存上一次匹配成功的结尾位置。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 800000;
int Next[N][26], fail[N], val[N], sz, n, op[N], pos[N], last[N], res[N][2];
char str[10], str2[N];
void init() {
memset(Next[0], 0, sizeof(Next[0]));
val[0] = 0;
sz = 1;
}
int insert(char *s) {
int u = 0, len = strlen(s);
for (int i = 0; i < len; i++) {
int k = s[i] - 'a';
if (!Next[u][k]) {
memset(Next[sz], 0, sizeof(Next[sz]));
val[sz] = i + 1;
Next[u][k] = sz++;
}
u = Next[u][k];
}
return u;
}
void getFail() {
queue<int> Q;
fail[0] = 0;
for (int i = 0; i < 26; i++)
if (Next[0][i]) {
fail[Next[0][i]] = 0;
Q.push(Next[0][i]);
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = 0; i < 26; i++) {
if (!Next[u][i])
Next[u][i] = Next[fail[u]][i];
else {
fail[Next[u][i]] = Next[fail[u]][i];
Q.push(Next[u][i]);
}
}
}
}
void query(char *s) {
int u = 0, len = strlen(s);
for (int i = 0; i < len; i++) {
u = Next[u][s[i] - 'a'];
int temp = u;
while (temp != 0) {
res[temp][0]++;
if (i - last[temp] >= val[temp]) {
res[temp][1]++;
last[temp] = i;
}
temp = fail[temp];
}
}
}
int main() {
int cas = 1;
while (scanf("%s", str2) == 1) {
init();
memset(res, 0, sizeof(res));
memset(last, -1, sizeof(last));
scanf("%d", &n);
printf("Case %d\n", cas++);
for (int i = 0; i < n; i++) {
scanf("%d%s", &op[i], str);
pos[i] = insert(str);
}
getFail();
query(str2);
for (int i = 0; i < n; i++)
printf("%d\n", res[pos[i]][op[i]]);
printf("\n");
}
return 0;
}