题面:BZOJ3172
2017.6.9 SA做法:
首先把论文给造出来是吧。。。(单词中间加空格好了)
然后在这篇文章(其实是一串字符串)求Height(后缀数组实现)
把Height数组求出来之后我们对于每个单词暴力向左向右找相邻的lcp,如果Height[i]大于等于单词长的话,那么这个串在那个后缀中出现了(而且是在前缀位置),如果小于了那就说明后面的都没有了,直接跳出
因为相邻的Height肯定比不相邻的Height大,所以具有连续的性质,所以可以证明这样是对的
把向左向右扫到的答案直接输出就行了
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
int n,m,q,rank[2000001],sa[2000001],a[2000001],rank1[2000001],h[2000001];
int ton[2000001],L[20001],R[20001];
char s[2000001],c[20001];
inline void Sort(){
for(int i=0;i<=m;i++)ton[i]=0;
for(int i=1;i<=n;i++)ton[rank[rank1[i]]]++;
for(int i=1;i<=m;i++)ton[i]+=ton[i-1];
for(int i=n;i;i--)sa[ton[rank[rank1[i]]]--]=rank1[i];
}
int main()
{
scanf("%d",&q);n=0;
for(int i=1;i<=q;i++){
scanf("%s",c+1);int l=strlen(c+1);
L[i]=n+1;R[i]=n+l;
for(int j=1;j<=l;j++)s[++n]=c[j];s[++n]=' ';
}n--;
for(int i=1;i<=n;i++)a[i]=s[i];
for(int i=1;i<=n;i++)rank[i]=a[i],rank1[i]=i;
m=127;Sort();
for(int i=1,j,p=1;p<n;i*=2,m=p){
for(p=0,j=n-i+1;j<=n;j++)rank1[++p]=j;
for(j=1;j<=n;j++)if(sa[j]>i)rank1[++p]=sa[j]-i;
Sort();swap(rank,rank1);rank[sa[1]]=p=1;
for(j=2;j<=n;j++)rank[sa[j]]=(rank1[sa[j]]==rank1[sa[j-1]]&&rank1[sa[j]+i]==rank1[sa[j-1]+i])?p:++p;
}
for(int i=1,j=0,k=0;i<=n;h[rank[i++]]=k)
for(k=k?k-1:k,j=sa[rank[i]-1];a[i+k]==a[j+k];++k);
for(int i=1;i<=q;i++){
int l=R[i]-L[i]+1;int ans=1;
for(int j=rank[L[i]];h[j]>=l;j--,ans++);
for(int j=rank[L[i]];h[j+1]>=l;j++,ans++);
printf("%d\n",ans);
}
return 0;
}
2017.12.10 AC自动机做法:
哇总算用AC自动机把这题过了QAQ
AC自动机做法这题死磕三天QAQ
说起来是很简单,很裸地跑一遍就好了
但是据说这题是在卡普通的AC自动机,Luogu一直90分,BZOJ一直TLE
第二天的时候发现我的AC自动机写法太慢了,于是换了一种写法
第三天又发现光是直接跳fail找串太慢了。。。于是把有串的节点记一下,然后跳的时候直接跳到这些点就好了QAQ
然后就AC了。。。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
inline int read(){
int k=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
return k*f;
}
inline void write(int x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);putchar(x%10+'0');
}
inline void writeln(int x){
write(x);puts("");
}
int n,nex[2000010][26],cnt=0,L=0,fail[2000010],failp[2000010],ans[2000010],rk[2000010],p[2000010],Cnt=0;
char s[2000010],z[2000010];
inline void insert(int x){
scanf("%s",s+1);int l=strlen(s+1);
int now=0;
for(int i=1;i<=l;i++)z[i+L]=s[i];
L+=l;z[++L]=' ';
for(int i=1;i<=l;i++){
int t=s[i]-'a';
if(!nex[now][t])nex[now][t]=++cnt;
now=nex[now][t];
}
if(!p[now])p[now]=++Cnt;
rk[x]=p[now];
}
queue<int>q;
inline void bfs(){
for(int i=0;i<26;i++)if(nex[0][i])q.push(nex[0][i]);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=0;i<26;i++)if(nex[now][i]){
fail[nex[now][i]]=nex[fail[now]][i];//记fail指针
failp[nex[now][i]]=p[fail[nex[now][i]]]?fail[nex[now][i]]:failp[fail[nex[now][i]]];//failp就是在记有fail的是原串结尾的节点
q.push(nex[now][i]);
}else nex[now][i]=nex[fail[now]][i];
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)insert(i);
bfs();
int now=0;
for(int i=1;i<=L;i++){
if(z[i]==' '){now=0;continue;}
int t=z[i]-'a';
if(nex[now][t])ans[p[nex[now][t]]]++;
int la=failp[nex[now][t]];//跳failp时间效率会快不少
while(la){
ans[p[la]]++;
la=failp[la];
}
now=nex[now][t];
}
for(int i=1;i<=n;i++)writeln(ans[rk[i]]);
return 0;
}