【BZOJ1212】[HNOI2004]L语言
题面
题解
其实可以不用AC自动机,但是最近在学就用了。。。
先把自动机建好,然后我们考虑怎么做。
设$vis[x]$表示文本串中$1-x$是否可以被表示出来
然后暴跳$fail$
设我们当前跳到了点$p$,在字符串的第$i$位
若有以$p$结尾的字符串且$vis[i-dep[p]]$,就证明$vis[i]$可以被拼出来了
好像做完了?然而并没有。
因为类似于$abcba$这样的串也会被判成由$abc$和$cba$构成的
所以我们要开一个差分数组看是否可以取到
体作可以看代码理解
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int MAX_N = 2e6;
const int MAX_M = 5000;
struct AC_Auto {
int c[MAX_M][26], fail[MAX_M], dep[MAX_M], cc[MAX_N], tot;
bool vis[MAX_N], End[MAX_M];
AC_Auto() : tot(0) { }
void insert(char *s) {
int o = 0;
for (int i = 1, l = strlen(s + 1); i <= l; i++) {
int son = s[i] - 'a';
if (!c[o][son]) c[o][son] = ++tot;
o = c[o][son];
dep[o] = i;
}
End[o] = 1;
}
void build() {
static queue<int> que;
for (int i = 0; i < 26; i++) if (c[0][i]) que.push(c[0][i]), fail[c[0][i]] = 0;
while (!que.empty()) {
int o = que.front(); que.pop();
for (int i = 0; i < 26; i++)
if (c[o][i]) fail[c[o][i]] = c[fail[o]][i], que.push(c[o][i]);
else c[o][i] = c[fail[o]][i];
}
}
int query(char *s) {
memset(vis, 0, sizeof(vis));
memset(cc, 0, sizeof(cc));
vis[0] = 1;
int l = strlen(s + 1);
for (int i = 1, o = 0; i <= l; i++) {
o = c[o][s[i] - 'a'];
for (int x = o; x; x = fail[x])
if (End[x] && vis[i - dep[x]]) {
vis[i] = 1;
cc[i - dep[x] + 1]++;
cc[i + 1]--;
}
}
int res = 0;
for (int i = 1, tot = 0; i <= l; i++) {
tot += cc[i];
if (tot) ++res; else break;
}
return res;
}
} ac;
int N, M; char s[MAX_N];
int main () {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++) scanf("%s", s + 1), ac.insert(s);
ac.build();
while (M--) {
scanf("%s", s + 1);
printf("%d
", ac.query(s));
}
return 0;
}