【YBTOJ】【Luogu P3121】[USACO15FEB]Censoring G
链接:
题目大意:
【Luogu P4824】[USACO15FEB]Censoring S的强化版。
在 \(S\) 中从头开始寻找屏蔽词,一旦找到一个屏蔽词,就删除它,然后又从头开始寻找(而不是接着往下找)。
有 \(n\) 个屏蔽词。
正文:
多模式串匹配,考虑用 AC 自动机。详见弱化版。
但是按朴素算法直接跳失配指针的话,复杂度就假了。所以还是建 fail
树然后跑 DFS。然后因为 trie 树的存储方式本来就很链表,所以不需用多余的维护。
代码:
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n;
char s[N], t[N], ans[N];
namespace AC
{
int t[N][30], val[N], cnt[N], fail[N], Len[N], stk[N], a[N];
int tot;
void Insert(char *s, int I)
{
int p = 0, len = strlen(s);
Len[I] = len;
for (int i = 0; i < len; i++)
{
int ch = s[i] - 'a';
if (!t[p][ch]) t[p][ch] = ++tot;
p = t[p][ch];
}
val[p] = I;
}
queue <int> q;
vector <int> e[N];
void Build()
{
while(!q.empty()) q.pop();
for (int i = 0; i < 26; i++)
if (t[0][i]) q.push(t[0][i]);
while (!q.empty())
{
int p = q.front(); q.pop();
for (int i = 0; i < 26; i++)
if (t[p][i])
fail[t[p][i]] = t[fail[p]][i], q.push(t[p][i]);
else
t[p][i] = t[fail[p]][i];
}
for (int i = 1; i <= tot; i++)
e[fail[i]].push_back(i);
}
void DFS(int u)
{
for (int i = 0; i < e[u].size(); i++)
{
int v = e[u][i];
if (val[u]) val[v] = val[u];
DFS(v);
}
}
void Query(char *s)
{
int p = 0, len = strlen(s), top = 0, m = 0;
for (int i = 0; i < len; i++)
{
p = t[p][s[i] - 'a'];
if (val[p]) a[i] = Len[val[p]], top -= Len[val[p]] - 1, p = stk[top];
else stk[++top] = p;
}
for (int i = len - 1, Dlt = 0; ~i; i--)
{
Dlt += a[i];
if (Dlt) Dlt--;
else ans[m++] = s[i];
}
for (int i = m - 1; ~i; i--) printf("%c", ans[i]);
}
}
int main()
{
scanf("%s", t);
n = Read();
for (int i = 1; i <= n; i++)
scanf ("%s", s), AC::Insert(s, i);
AC::Build();
AC::DFS(0);
AC::Query(t);
return 0;
}