感觉后缀自动机好难啊,比后缀数组难多了...看了好几天勉强懂了一丢丢
学习博客:
题目传送门:https://www.spoj.com/problems/SUBLEX/en/
题意:给你一个字符串,然后是q次查询,每次输出第i小的字符串。
题解:先构造出后缀自动机,然后对每个节点预处理出从这个节点可以到达多少个不同的子串,然后沿着SAM一路查找下去并更新。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=2e6+10; 5 6 struct state 7 { 8 int mxlen,link; 9 int cnt; 10 int nt[26]; 11 }st[maxn<<1]; 12 13 int sz,last; 14 char s[maxn]; 15 16 inline void sam_init() 17 { 18 st[0].mxlen=0; 19 st[0].link=-1; 20 sz=1; 21 last=0; 22 } 23 24 inline void sam_extend(int c) 25 { 26 c-='a'; 27 int cur=sz++; 28 st[cur].mxlen=st[last].mxlen+1; 29 st[cur].cnt=1; 30 int p=last; 31 while(p!=-1&&!st[p].nt[c]){ 32 st[p].nt[c]=cur; 33 p=st[p].link; 34 } 35 if(p==-1) st[cur].link=0; 36 else{ 37 int q=st[p].nt[c]; 38 if(st[p].mxlen+1==st[q].mxlen) st[cur].link=q; 39 else{ 40 int clone=sz++; 41 st[clone].mxlen=st[p].mxlen+1; 42 memcpy(st[clone].nt,st[q].nt,sizeof(st[q].nt)); 43 st[clone].link=st[q].link; 44 while(p!=-1&&st[p].nt[c]==q){ 45 st[p].nt[c]=clone; 46 p=st[p].link; 47 } 48 st[q].link=st[cur].link=clone; 49 } 50 } 51 last=cur; 52 } 53 54 int b[maxn],c[maxn]; 55 56 void build() //这个难道不是基排吗,为啥看好多博客写拓扑排序,,, 57 { 58 int len=strlen(s+1); 59 for(int i=1;i<=len;i++) sam_extend(s[i]); 60 for(int i=1;i<=sz;i++) c[st[i].mxlen]++; 61 for(int i=1;i<=sz;i++) c[i]+=c[i-1]; 62 for(int i=1;i<=sz;i++) b[c[st[i].mxlen]--]=i; 63 for(int i=sz;i>=1;i--){ 64 st[b[i]].cnt=1; 65 for(int j=0;j<26;j++) 66 st[b[i]].cnt+=st[st[b[i]].nt[j]].cnt; //st[].cnt记录当前节点不同子串个数 67 } 68 } 69 70 void query(int k) 71 { 72 int p=0; 73 while(k){ 74 for(int i=0;i<26;i++){ 75 if(st[p].nt[i]){ 76 if(k>st[st[p].nt[i]].cnt) k-=st[st[p].nt[i]].cnt; 77 else { 78 char ch=i+'a'; 79 cout<<ch; 80 p=st[p].nt[i]; 81 k--; 82 break; 83 } 84 } 85 } 86 } 87 } 88 89 int main() 90 { 91 ios::sync_with_stdio(false); 92 cin.tie(0); 93 cout.tie(0); 94 cin>>s+1; 95 sam_init(); 96 build(); 97 int m; 98 cin>>m; 99 while(m--){ 100 int n; 101 cin>>n; 102 query(n); 103 cout<<endl; 104 } 105 return 0; 106 }
有一个地方我一直没搞清楚,这两种写法不一样吗?一种是父亲加上他,一种是它加儿子,最后结果应该是一样的吧...但是第一种写法是错的。
1 for(int i=sz;i>=1;i--) st[i].cnt=1; 2 for(int i=sz;i>=1;i--) st[st[b[i]].link].cnt+=st[b[i]].cnt;
1 for(int i=sz;i>=1;i--){ 2 st[b[i]].cnt=1; 3 for(int j=0;j<26;j++) 4 st[b[i]].cnt+=st[st[b[i]].nt[j]].cnt; 5 }