【GDOI2017 day1 T3】微信 && SAM学习小记

来源:互联网 发布:算法之道实验指导书 编辑:程序博客网 时间:2024/05/16 05:12

SAM学习小记

【GDOI2017 day1 T3】微信

Description
Description
Input
Input
Output
Output
Sample Input

3idont<<<<loveyoui<youlovemelovingyou2110111

Sample Output

43

Data Constraint
Data


一道字符串题,比赛以前,只会KMP,ExKMP,SA,Manachar,Trie。。。一看这道题,放入Trie中,然后呢??


出题人说,用广义后缀自动机。啊啊啊?没学过啊

现在终于学了SAM,再看看这道题——水题!!!


后缀自动机(Suffix Automaton)

后缀自动机是一个用O(n)的复杂度,以遍历DAG的方式表示出一个或多个字符串的所有子串的东东

SAM中元素的一些成分

  • S:原串
  • x:DAG中的x节点其中rootx的最长路径为原串x的前缀
  • nxt[x][c]:节点x所代表的串中拓展一个字符c到达的节点
  • len[x]:rootx的最长路径长度
  • pre[x]:x节点代表的前缀的最长后缀为pre[x]代表的前缀
  • right(s):S中的所有子串s的末尾位置集合
  • 节点x所表示的子串:rootx的所有路径代表的字符串

构造方式

make

注意根为1

实例:abacb:
①插入a
Step1
②这时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
Step5

复杂度

O(n),不超过2n
proof

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;        }    }}

性质

  1. 状态x表示的长度范围(lenprex,lenx]
  2. 一个状态代表的不同字符串在原串中出现次数相等,且每次出现位置末位相同
  3. right(x)right(fax)
  4. 后缀自动机的fa树是原串的反向前缀树
  5. 两个串的最长公共后缀位于所在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;}
原创粉丝点击