P4824 [USACO15FEB]Censoring (Silver) 审查(银)&&P3121 [USACO15FEB]审查(黄金)Censoring (Gold)
P3121 [USACO15FEB]审查(黄金)Censoring (Gold) (银的正解是KMP)
AC自动机+栈
多字符串匹配--->AC自动机
删除单词的特性--->栈
所以我们先打个AC自动机模板
然后搞2个栈维护:
- AC自动机目前跑到字典树上的哪个点
- 已经跑过且没被删除的字符(答案栈)
每次碰到有结尾标记的点,就让2个栈弹出这个点所对应的单词的长度
最后输出第二个栈就行了
attention:输出答案后要换行,否则会蜜汁爆炸7pts
P3121 code(P4824要稍作修改):
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; struct data{ int nxt[26],fail/*,last*/,end; }a[100005]; int n,cnt,top,stk1[100005],stk2[100005]; char g[100005],q[100005]; inline void Trie_build(){ scanf("%s",q); int u=0,len=strlen(q); for(int i=0;i<len;++i){ int p=q[i]-'a'; if(!a[u].nxt[p]) a[u].nxt[p]=++cnt; u=a[u].nxt[p]; }a[u].end=len; } void AC_build(){ queue <int> h; for(int i=0;i<26;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]); while(!h.empty()){ int x=h.front(); h.pop(); for(int i=0;i<26;++i){ int &to=a[x].nxt[i]; if(to){ a[to].fail=a[a[x].fail].nxt[i]; //a[to].last= a[a[to].fail].end ? a[to].fail:a[a[to].fail].last; h.push(to); }else to=a[a[x].fail].nxt[i]; } } }
//---------以上裸AC自动机------------ void query(){ int u=0,len=strlen(g); for(int i=0;i<len;++i){ u=a[u].nxt[g[i]-'a']; stk1[++top]=u; //栈1存当前点在字典树中的位置 stk2[top]=i; //栈2存字符(答案)在主串中所在的位置 while(a[u].end) top-=a[u].end,u= top ? stk1[top]:0; //删除操作 } for(int i=1;i<=top;++i) putchar(g[stk2[i]]); putchar('\n'); //不换行就蜜汁爆炸(大雾) } int main(){ scanf("%s",g); scanf("%d",&n); for(int i=1;i<=n;++i) Trie_build(); AC_build(); query(); return 0; }