[题解]P3966 [TJOI2013] 单词

P3966 [TJOI2013] 单词

对所有模式串建立AC自动机。

\(p[i]\)来表示经过节点\(i\)的字符串个数。

那么节点\(u\)的答案就是fail树上,以\(u\)为根的子树的\(p\)之和。

由于我们已经计算了\(p[i]\),所以字符串\(i\)作为模式串本身&模式串前缀的情况已经考虑了。还需考虑\(i\)作为模式串后缀的情况,而只有fail树上子树\(i\)的节点才有\(i\)这个后缀,所以子树\(i\)\(p\)之和就是节点\(i\)的答案。用拓扑排序一路更新到根节点即可。

点击查看代码
#include<bits/stdc++.h>
#define T 210
#define N 1000010
#define S 26
using namespace std;
int n,tr[N][S],fail[N],cnt,ans[N],pos[T],deg[N];
queue<int> q;
string s;
void ins(string s,int num){
int p=0;
for(char i:s){
int c=i-'a';
if(!tr[p][c]) tr[p][c]=++cnt;
p=tr[p][c];
ans[p]++;
}
pos[num]=p;
}
void get_fail(){
for(int i=0;i<26;i++)
if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[u][i])
fail[tr[u][i]]=tr[fail[u]][i],
q.push(tr[u][i]),deg[fail[tr[u][i]]]++;
else tr[u][i]=tr[fail[u]][i];
}
}
}
void topo(){
for(int i=1;i<=cnt;i++) if(!deg[i]) q.push(i);
while(!q.empty()){
int t=q.front();
q.pop();
ans[fail[t]]+=ans[t],deg[fail[t]]--;
if(!deg[fail[t]]) q.push(fail[t]);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>s;
ins(s,i);
}
get_fail();
topo();
for(int i=1;i<=n;i++)
cout<<ans[pos[i]]<<"\n";
return 0;
}

还有一种方法可以不用拓扑排序,就是用数组来模拟get_fail()中的队列,这样队头到队尾一定是BFS序,所以反过来,从队尾到队头一定是一个拓扑序,所以更新的时候不用拓扑排序,仅需用一个循环,从队尾遍历到队头,更新答案即可。

点击查看代码
#include<bits/stdc++.h>
#define T 210
#define N 1000010
#define S 26
using namespace std;
int n,tr[N][S],fail[N],cnt,ans[N],pos[T],q[N];
string s;
void ins(string s,int num){
int p=0;
for(char i:s){
int c=i-'a';
if(!tr[p][c]) tr[p][c]=++cnt;
p=tr[p][c];
ans[p]++;
}
pos[num]=p;
}
void get_fail(){
int head=1,tail=0;
for(int i=0;i<26;i++)
if(tr[0][i]) q[++tail]=tr[0][i];
while(head<=tail){
int u=q[head++];
for(int i=0;i<26;i++){
if(tr[u][i])
fail[tr[u][i]]=tr[fail[u]][i],
q[++tail]=tr[u][i];
else tr[u][i]=tr[fail[u]][i];
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>s;
ins(s,i);
}
get_fail();
for(int i=cnt;i>=1;i--) ans[fail[q[i]]]+=ans[q[i]];
for(int i=1;i<=n;i++)
cout<<ans[pos[i]]<<"\n";
return 0;
}
posted @   Sinktank  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2025-3-6 6:10:53 TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.
点击右上角即可分享
微信分享提示