P3966 [TJOI2013]单词
AC自动机
一看就知道是AC自动机
它问的是每一个单词在所有单词中出现的次数
对所有单词搞一个AC自动机
然后把所有单词连在一起,中间用一个不会出现的字符或者其他什么的隔开
然后把连在一起的串放到AC自动机上搞匹配就行了
要注意单词可能有重复
所以要把重复的单词判一下
具体看代码
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> using namespace std; const int N=1e6+7; int n,mp[207]; char s[N]; int c[N][27],pd[N],fail[N],g[N],cnt; char a[N]; int b[N+207],len; inline void ins(int num)//插入单词 { int u=0,l=strlen(a); for(int i=0;i<l;i++) { int v=a[i]-'a'+1; b[++len]=v;//把匹配串处理一下 if(!c[u][v]) c[u][v]=++cnt; u=c[u][v]; } if(!pd[u]) pd[u]=num; mp[num]=pd[u];//用来处理重复的单词,mp[i]表示第i个单词最早出现时的位置 b[++len]=-1;//单词之间用-1隔开 } queue <int> q; inline void pre()//预处理fail和g,g[x]表示从x一直走失配边,走到的第一个结束标记的位置 { for(int i=1;i<=26;i++) if(c[0][i]) q.push(c[0][i]); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=1;i<=26;i++) { int v=c[u][i]; if(!v) c[u][i]=c[fail[u]][i]; else { fail[v]=c[fail[u]][i]; g[v]= pd[fail[v]] ? fail[v] : g[fail[v]]; q.push(v); } } } } int ans[207]; inline void query()//匹配 { int u=0; for(int i=1;i<=len;i++) { if(b[i]==-1)//如果走到分隔标记,就回到起点匹配 { u=0; continue; } u=c[u][b[i]]; if(pd[u]) ans[pd[u]]++; for(int j=g[u];j;j=g[j]) ans[pd[j]]++; } for(int i=1;i<=n;i++) printf("%d\n",ans[mp[i]]); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",a); ins(i); } pre(); query(); return 0; }