USACO 2017 December Contest Platinum T1: Standing Out from the Herd
题目大意
定义一个字符串的「独特值」为只属于该字符串的本质不同的非空子串的个数。如 "amy" 与 “tommy” 两个串,只属于 "amy" 的本质不同的子串为 "a" "am" "amy" 共 3 个。只属于 "tommy" 的本质不同的子串为 "t" "to" "tom" "tomm" "tommy" "o" "om" "omm" "ommy" "mm" "mmy" 共 11 个。 所以 "amy" 的「独特值」为 3 ,"tommy" 的「独特值」为 11 。
给定 N (N≤1e5) 个字符集为小写英文字母的字符串,所有字符串的长度和小于 1e5,求出每个字符串「独特值」。
题目分析
看到这种有多个字符串的题,我们一般会去考虑使用广义SAM或把每个字符串连起来中间以特殊字符隔开。
这里考虑广义SAM,其实就是每次加入新字符串时把lst置为1就行。
我们对这些字符串建出广义SAM,每一个串都从根节点开始遍历,我们沿着ch数组往下跳,每到一个新节点时,再沿着parent树往上跳,这样可以保证遍历它的所有子串。
最后统计一下只经过一次的节点 i 有哪些,把它的贡献 l[i]-l[fa[i]] 加给对应字符串即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN=2e5+10; 5 6 int n,tot; 7 int len[MAXN],s[MAXN]; 8 9 struct Suffix_Automation{ 10 int lst,cnt; 11 int ch[MAXN][27],fa[MAXN],l[MAXN],vis[MAXN],ans[MAXN]; 12 Suffix_Automation(){lst=cnt=1;} 13 inline void Extend(int x){ 14 int p=lst,np=++cnt;lst=np; 15 l[np]=l[p]+1; 16 while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p]; 17 if(!p) fa[np]=1; 18 else{ 19 int q=ch[p][x]; 20 if(l[q]==l[p]+1) fa[np]=q; 21 else{ 22 int nq=++cnt; 23 memcpy(ch[nq],ch[q],sizeof(ch[q])); 24 l[nq]=l[p]+1; 25 fa[nq]=fa[q]; 26 fa[q]=fa[np]=nq; 27 while(ch[p][x]==q) ch[p][x]=nq,p=fa[p]; 28 } 29 } 30 } 31 inline void Update(int x,int y){ 32 for(;x&&vis[x]!=y&&vis[x]!=-1;x=fa[x]){ 33 if(vis[x]!=0) vis[x]=-1; 34 else vis[x]=y; 35 } 36 } 37 inline void Work(){ 38 tot=0; 39 for(int i=1;i<=n;++i) 40 for(int j=1,x=1;j<=len[i];++j) 41 Update(x=ch[x][s[++tot]],i); 42 for(int i=1;i<=cnt;++i) 43 if(vis[i]!=-1){ 44 int x=vis[i]; 45 ans[x]+=l[i]-l[fa[i]]; 46 } 47 for(int i=1;i<=n;++i) 48 printf("%d\n",ans[i]); 49 } 50 }SAM; 51 52 int main(){ 53 cin>>n;getchar(); 54 for(int i=1;i<=n;++i){ 55 char ch; 56 SAM.lst=1; 57 while((ch=getchar())!='\n'&&ch!=EOF){ 58 ch-='a'; 59 ++len[i];s[++tot]=ch; 60 SAM.Extend(ch); 61 } 62 } 63 SAM.Work(); 64 return 0; 65 }