[Usaco2015 Feb]Censoring
Censoring
FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过10^5的字符串S。他有一个包含n个单词的列表,列表里的n个单词记为t_1...t_N。他希望从S中删除这些单词。
FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操作并输出最后的S
给若干模式串和一个文本串.每次从文本串开头找到一个模式串,将其删去,直到无法删去为止,求出最后剩余的文本.保证任一个模式串中没有其他模式串.
jklover的题解
将模式串建成一个 AC 自动机,用一个栈来维护当前剩余的字符.匹配成功时直接修改栈顶.同时需要维护每个节点在 Trie 树上的位置.这样删除后可以立即得出当前的位置 u
时间复杂度:线性。
用栈来维护s,想法实在巧妙。
co int N=1e5+1,S=26;
int len[N];
namespace AC
{
int idx;
int ch[N][S],fail[N],val[N];
void ins(char*s,int n,int v)
{
int u=0;
for(int i=0;i<n;++i)
{
int k=s[i]-'a';
if(!ch[u][k])
ch[u][k]=++idx;
u=ch[u][k];
}
val[u]=v;
}
void getfail()
{
std::queue<int>Q;
for(int i=0;i<S;++i)
if(ch[0][i])
Q.push(ch[0][i]);
while(Q.size())
{
int u=Q.front();Q.pop();
for(int i=0;i<S;++i)
{
if(ch[u][i])
{
fail[ch[u][i]]=ch[fail[u]][i];
Q.push(ch[u][i]);
}
else
ch[u][i]=ch[fail[u]][i];
}
}
}
void solve(char*s,int n)
{
int u=0;
char stk[N];
int tp=0;
int lst[N];
for(int i=0;i<n;++i)
{
int k=s[i]-'a';
stk[++tp]=s[i];
lst[tp]=ch[u][k];
if(val[lst[tp]])
tp-=val[lst[tp]];
u=lst[tp];
}
for(int i=1;i<=tp;++i)
putchar(stk[i]);
puts("");
}
}
char buf[N];
char s[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%s",s);
int n;
read(n);
for(int i=1;i<=n;++i)
{
scanf("%s",buf);
int len=strlen(buf);
AC::ins(buf,len,len);
}
AC::getfail();
AC::solve(s,strlen(s));
return 0;
}
静渊以有谋,疏通而知事。