SAM学习小记
【GDOI2017 day1 T3】微信
Description
Input
Output
Sample Input
3
idont<<<<loveyou
i<youloveme
lovingyou
2
110
111
Sample Output
4
3
Data Constraint
一道字符串题,比赛以前,只会KMP,ExKMP,SA,Manachar,Trie。。。一看这道题,放入Trie中,然后呢??
出题人说,用广义后缀自动机。啊啊啊?没学过啊
现在终于学了SAM,再看看这道题——水题!!!
后缀自动机(Suffix Automaton)
后缀自动机是一个用O(n)的复杂度,以遍历DAG的方式表示出一个或多个字符串的所有子串的东东
SAM中元素的一些成分
- S:原串
- x:DAG中的x节点其中 root→x 的最长路径为原串x的前缀
- nxt[x][c]:节点x所代表的串中拓展一个字符c到达的节点
- len[x]: root→x 的最长路径长度
- pre[x]:x节点代表的前缀的最长后缀为pre[x]代表的前缀
- right(s):S中的所有子串s的末尾位置集合
- 节点x所表示的子串: root→x 的所有路径代表的字符串
构造方式
注意根为1
实例:abacb:
①插入a
②这时las=2,插入b,2的fa链2-1,nxt[2][b]为空,nxt[2][b]=now,跳到1,重复执行操作,跳到0,结束,fa[now]=1
③插入a,las=3,las的fa链为3-1,nxt[3][a]为空,nxt[3][a]=now,跳到1,nxt[1][a]不为空,结束,nxt[1][a]=2,len[1]+1==len[2],fa[4]=2
④插入c
⑤插入b,nxt[4][b]=now,nxt[1][b]不为空,停止,nxt[1][b]=3,但len[1]+1!=len[3],新开节点7代表3(‘b’),1的fa链nxt[][b]=3的只有1,nxt[1][b]=7,fa[7]=fa[3],fa[now]=fa[3]=7
复杂度
O(n),不超过2n
int nw(int x){deep[++cnt]=x+1;/*deep就是len*/ return cnt;}
void ins(int las,int c){
int now=nw(deep[las]);
for(;las && !tr[las].nxt[c];las=tr[las].pre)tr[las].nxt[c]=now;
if(!las)tr[now].pre=1;/*pre也就是fa*/ else{
int u=las,v=tr[u].nxt[c];
if(deep[u]+1==deep[v])tr[now].pre=v;else{
int _v=nw(deep[u]);tr[_v]=tr[v];tr[now].pre=tr[v].pre=_v;
for(;u && tr[u].nxt[c]==v;u=tr[u].pre)tr[u].nxt[c]=_v;
}
}
}
性质
- 状态x表示的长度范围 (lenprex,lenx]
- 一个状态代表的不同字符串在原串中出现次数相等,且每次出现位置末位相同
- right(x)⊆right(fax)
- 后缀自动机的fa树是原串的反向前缀树
- 两个串的最长公共后缀位于所在fa树上的节点的最长公共祖先
求right:
大小:插入新字符时++count[now]
,最后按照DAG的深度count[fa[x]]+=count[x]
广义后缀自动机
其实就是trie上自动机,每次就在trie_parent[x]所在自动机上的节点后加上x就好了,构造方式完全一样
回到原题
Err?不就是道水题吗
将所有出现的串压到trie中,放入状态,再做广义后缀自动机就好了。
#include<cstring>
#include<cstdio>
#define M 1001001
#define full 1048576
#define max(a,b) (a>b?a:b)
#define lowbit(x) (x&(-x))
using namespace std;
int f[full],cnt,tot,go[M][26],sta[M],n,q,root,p[M],fa[M],que[M],h,t,deep[M+M],v[M+M],b[M+M];
struct SAM{int nxt[26],pre,sta;}tr[M+M];
char s[M];
bool dealt[full];
int nw(int x){deep[++cnt]=x+1;return cnt;}
void ins(int las,int c,int sta,int &to){
int now=nw(deep[las]);tr[now].sta|=sta;to=now;
for(;las && !tr[las].nxt[c];las=tr[las].pre)tr[las].nxt[c]=now;
if(!las)tr[now].pre=1;else{
int u=las,v=tr[u].nxt[c];
if(deep[u]+1==deep[v])tr[now].pre=v;else{
int _v=nw(deep[u]);tr[_v]=tr[v];tr[now].pre=tr[v].pre=_v;
for(;u && tr[u].nxt[c]==v;u=tr[u].pre)tr[u].nxt[c]=_v;
}
}
}
int main(){
freopen("wechat.in","r",stdin);
freopen("wechat.out","w",stdout);
scanf("%d\n",&n);cnt=p[root=0]=1;tr[1].sta=(1<<n)-1;
for(int i=1;i<=n;i++){
scanf("%s",s+1);sta[root]+=(1<<i-1);
for(int t=1,len=strlen(s+1),now=0;t<=len;t++){
if(s[t]=='<')now=fa[now];else{
int x=s[t]-97,y;if(!go[now][x])go[now][x]=++tot;
y=go[now][x];sta[y]|=1<<i-1;fa[y]=now;now=y;
}
}
}
for(h=0,que[t=1]=0;h!=t;)
for(int x=que[++h],i=0;i<26;i++)if(go[x][i])ins(p[x],i,sta[que[++t]=go[x][i]],p[go[x][i]]);
for(int i=1;i<=cnt;i++)b[deep[i]]++;
for(int i=1;i<=cnt;i++)b[i]+=b[i-1];
for(int i=1;i<=cnt;i++)v[b[deep[i]]--]=i;
for(int i=cnt;i;i--)tr[tr[v[i]].pre].sta|=tr[v[i]].sta,f[tr[v[i]].sta]=max(f[tr[v[i]].sta],deep[v[i]]);
for(dealt[que[t=1]=(1<<n)-1]=1,h=0;h!=t;)for(int x=que[++h],T=x,i=lowbit(T);T;T-=i,i=lowbit(T)){
f[x-i]=max(f[x-i],f[x]);if(!dealt[x-i])dealt[que[++t]=x-i]=1;
}
scanf("%d\n",&q);
for(int i=1;i<=q;i++){
int x=0;
for(int j=1;j<=n;j++)if(getchar()-48)x+=1<<j-1;
printf("%d\n",f[x]);scanf("\n");
}
fclose(stdin);fclose(stdout);
return 0;
}