[BZOJ3473]字符串
话说广义后缀自动机原来是这么建的...
这道题算是一道广义后缀自动机的模板题,和后缀数组的感觉差不多啊.
这题的一个难点在于如何求出每个节点的子串在多少个字符串中出现过.
原本的想法是差分,后来发现我SB了,差分只能解决数量上的问题,不能解决这种存在性的问题(?).
本来想搞一个Set玩启发式合并,但是貌似可以用一种类似于暴力的方法(?),对于当前跑的这个字符串,在后缀自动机上跑到了now,
那么就把now的所有祖先节点全部打上标记,如果遇到了打上标记的点,直接break就行了,复杂度貌似N乘根号N.而且这是最坏情况.
最后统计答案即可.
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<string> #include<cmath> #include<ctime> #include<algorithm> #include<map> #include<set> #include<queue> #include<iomanip> using namespace std; #define ll long long #define db double #define up(i,j,n) for(int i=j;i<=n;i++) #define pii pair<int,int> #define uint unsigned int #define FILE "dealing" #define eps 1e-4 int read(){ int x=0,f=1,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } template<class T> bool cmax(T& a,T b){return a<b?a=b,true:false;} template<class T> bool cmin(T& a,T b){return a>b?a=b,true:false;} const int maxn=100060,limit=50100,inf=1000000000,r=3,mod=1000000007; int n,k; string s[maxn]; namespace SAM{ const int maxn=201000; int np,nq,q,p,now,cnt=1,sa[maxn],c[maxn][26],val[maxn],count[maxn],pre[maxn],len[maxn],v[maxn],f[maxn],ccnt[maxn]; vector<int> E[maxn]; int extend(int x){ p=now;np=now=++cnt;len[np]=len[p]+1; while(p&&!c[p][x])c[p][x]=np,p=pre[p]; if(!p)pre[np]=1; else { q=c[p][x]; if(len[q]==len[p]+1)pre[np]=q; else{ len[nq=++cnt]=len[p]+1; memcpy(c[nq],c[q],sizeof(c[q])); pre[nq]=pre[q]; pre[q]=pre[np]=nq; while(p&&c[p][x]==q)c[p][x]=nq,p=pre[p]; } } return np; } void build(int id){ now=1; int n=s[id].size(); up(i,0,n-1)val[extend(s[id][i]-'a')]=id; } void prepare(){ up(i,1,cnt)count[len[i]]++; up(i,1,cnt)count[i]+=count[i-1]; for(int i=cnt;i>=1;i--)sa[count[len[i]]--]=i; up(i,1,cnt){ int x=sa[i]; f[x]=f[pre[x]]+(ccnt[x]>=k)*(len[x]-len[pre[x]]); } } int Len=0; int walk(int x){ while(pre[now]&&!c[now][x])now=pre[now],Len=len[now]; if(!c[now][x])return 0; now=c[now][x];Len++; return Len; } int vis[maxn]; void GO(int id){ int n=s[id].size(); now=1;Len=0;vis[now]=id; up(i,0,n-1){ int x=s[id][i]-'a'; walk(x); int tmp=now; for(;vis[tmp]!=id;tmp=pre[tmp]){ vis[tmp]=id; ccnt[tmp]++; } } } }; int main(){ freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); n=read(),k=read(); up(i,1,n){ cin>>s[i]; SAM::build(i); } up(i,1,n)SAM::GO(i); SAM::prepare(); ll ans=0; up(i,1,n){ ans=0; int m=s[i].size(); SAM::now=1;SAM::Len=0; up(j,0,m-1){ SAM::walk(s[i][j]-'a'); ans+=SAM::f[SAM::now]; } printf("%lld ",ans); } return 0; }