1 条题解
-
0
自动搬运
来自洛谷,原作者为

Treeloveswater
**搬运于
2025-08-24 21:45:30,当前版本为作者最后更新于2017-03-15 10:57:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
既然没人搞题解,那我就来份题解吧。
这个题实际上是用栈+AC自动机实现O(n)效率的。
再次吐槽一下数据,纯暴力都能过93分,数据水的不要不要的。
两种做法:
##法1:栈+AC自动机
开两个栈,一个记录当前节点扫到AC自动机的哪个地方了,一个记录当然栈内合法的字符,每当发现一个能删去的字符串的时候,只需要把两个栈都减去字符串长度即可,把now修改一下,继续往后扫即可,最后输出第二个栈即可。
##法2:暴力玄学AC自动机
不用栈,sign记录当前节点扫到AC自动机的哪个地方,我们开一个next数组和pre数组,记录当前这个点的后面那个点是几号、前面那个点是几号,每当发现一个能删去的字符串的时候,暴力跳到字符串头上的pre,将其的next修改为字符串尾的next,修改一下now,继续扫就行,这样也能A。因为要O(n)预处理一下next和pre,所以会比法1稍微慢一丢丢。(鬼知道我为啥玄学的法2卡评测机的比法1跑的快几毫秒)
##法1代码:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> using namespace std; char s[100001],ori[100001]; int n,tot,w,top; int trie[100001][26],fail[100001],heap[100001],sign[100001]; int isend[100001]; void insert(char *s){ int now=0,len=strlen(s); for(int i=0;i<len;i++){ int x=s[i]-'a'; if(!trie[now][x])trie[now][x]=++tot; now=trie[now][x]; } isend[now]=len; } void makefail(){ queue<int> q; for(int i=0;i<26;i++) if(trie[0][i])q.push(trie[0][i]); while(!q.empty()){ int now=q.front();q.pop(); for(int i=0;i<26;i++){ if(!trie[now][i]){ trie[now][i]=trie[fail[now]][i]; continue; } fail[trie[now][i]]=trie[fail[now]][i]; q.push(trie[now][i]); } } } void solve(char *s){ int now=0,len=strlen(s),i=0; w=0; while(i<len){ int x=s[i]-'a'; now=trie[now][x]; sign[++top]=now; heap[top]=i; if(isend[now]){ top-=isend[now]; if(!top) now=0; else now=sign[top]; } i++; } } int main() { scanf("%s",s); scanf("%d",&n); int len=strlen(s); for(int i=1;i<=n;i++){ scanf("%s",ori); insert(ori); } makefail(); solve(s); for(int i=1;i<=top;i++) printf("%c",s[heap[i]]); return 0; }
- 1
信息
- ID
- 2185
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者