链接
题意
青岛网络赛的题目,其实挺简单的裸题,不知道为什么没看到。。。还好最后申进去了。
给出一些关键词和一个长文本,需要将长文本中出现过关键词的地方用’*’代替。
题解
AC自动机的裸题,典型的多文本匹配。
在建trie树的时候需要传递关键词的长度len,insert后nd[now]=len。这样在建好自动机匹配的时候可以记录cover[i],表示i位置出现的长度为cover[i]的一个文本。
扫描替换的时候记录还能cover多少个字符即可。
代码
#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <cctype>
#include <algorithm>
using namespace std;
#define maxn (1000010)
char keyword[maxn], text[maxn];
int cover[maxn];
struct Aho
{
#define maxsz (maxn)
#define degree (26)
int next[maxsz][26], nd[maxsz], fail[maxsz];
int root, L;
int newnode()
{
for(int i = 0; i < degree; i++)
next[L][i] = -1;
nd[L] = 0;
return L++;
}
void init()
{
L = 0;
root = newnode();
}
void insert(char s[])
{
int now = root, sz = strlen(s);
for(int i = 0, key; i < sz; i++)
{
key = s[i] - 'a';
if(next[now][key] == -1)
next[now][key] = newnode();
now = next[now][key];
}
nd[now] = sz;
}
void build()
{
queue<int> que;
fail[root] = root;
for(int i = 0; i < degree; i++)
if(next[root][i] == -1)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
que.push(next[root][i]);
}
while(!que.empty())
{
int now = que.front();
que.pop();
for(int i = 0; i < degree; i++)
if(next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]] = next[fail[now]][i];
que.push(next[now][i]);
}
}
}
void check(char s[], int sz)
{
int now = root, tmp;
for(int i = 0, key; i < sz; i++)
{
if(!isalpha(s[i])) { now = root; continue; }
key = tolower(s[i]) - 'a';
tmp = now = next[now][key];
while(tmp)
{
cover[i-nd[tmp]+1] = max(nd[tmp], cover[i-nd[tmp]+1]);
tmp = fail[tmp];
}
}
}
} actree;
int main()
{
int T;
cin >> T;
while(T--)
{
actree.init();
int n;
cin >> n;
while(n--)
{
scanf("%s", keyword);
actree.insert(keyword);
}
actree.build();
while(getchar() != '\n');
gets(text);
int len = strlen(text);
actree.check(text, len);
for(int i = 0, k = 0; i < len; i++)
{
if(cover[i]) { k = max(k, cover[i]); cover[i] = 0; }
if(k) { text[i] = '*'; k--; }
}
printf("%s\n", text);
}
return 0;
}