【SPOJ7258】Lexicographical Substring Search-后缀自动机+拓补序递推

来源:互联网 发布:网络报纸取代传统报纸 编辑:程序博客网 时间:2024/05/19 05:33

测试地址:Lexicographical Substring Search

题目大意:给定一个字符串,有Q个询问,每个询问字符串中字典序第Ki小的本质不同的子串。

做法:这几天学习了后缀自动机(Suffix Automaton,SAM),个人感觉除了clj的原论文,写得最好的就是这个,我就是看着这个学会后缀自动机的。里面最后讲的一道例题就是这道题,首先对字符串构建后缀自动机,然后对于每个点维护一个值s,指从这个点出发最多能找到的子串数,显然我们可以按原图的反向拓补序依次求出每一个点的s,所以我们可以用类似记忆化搜索的方法递推,搜索到没算过的点继续往下算,算过的就直接累加,当然从一个点出发能找到的子串也包括这个点自身,所以回来时记得+1。然后对于每个询问,从起始节点开始,按照字典序从小到大扫过每个儿子,如果K大于当前儿子能找到的子串数,则将K减去这个数,然后找下一个儿子,直到找到一个儿子使得从它出发能找到的子串数≥K,就输出这个儿子的字母,然后从这个儿子往下寻找。因为从一个点出发能找到的子串也包括这个点自身,所以每走过一个点,都要把K减去1,当K等于0的时候就结束。这样,总的时间复杂度就是O(NQ),可以通过。

我犯二的地方:第一次写想错了,把最后一个插入的点当成最后一个可以接受后缀的点......显然错了,要分清楚这两个概念。

以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;char s[100010];int n,q,tot=0,in[200010]={0},last=0;struct SAMnode{  int pre,ch[30],step,s;}nd[200010];bool vis[200010]={0};void extend(int c){  int p=last,q,np=++tot,nq;  nd[np].step=nd[p].step+1;  for(int i=0;i<=25;i++) nd[np].ch[i]=-1;  while(nd[p].ch[c]==-1&&p!=-1)  {    nd[p].ch[c]=np;p=nd[p].pre;  }  if (p!=-1)  {    q=nd[p].ch[c];if (nd[q].step==nd[p].step+1){  nd[np].pre=q;}else{  nq=++tot;  nd[nq].pre=nd[q].pre;  for(int i=0;i<=25;i++) nd[nq].ch[i]=nd[q].ch[i];  nd[nq].step=nd[p].step+1;  nd[nq].s=1;  nd[q].pre=nq,nd[np].pre=nq;  while(p!=-1&&nd[p].ch[c]==q)  {    nd[p].ch[c]=nq;p=nd[p].pre;  }}  }  else nd[np].pre=0;  nd[np].s=1;  last=np;}void calc_s(int v){  for(int i=0;i<=25;i++)    if (nd[v].ch[i]!=-1){  if (!vis[nd[v].ch[i]]) calc_s(nd[v].ch[i]);  nd[v].s+=nd[nd[v].ch[i]].s;}  vis[v]=1;}void build(){  nd[0].pre=-1;nd[0].s=0;nd[0].step=0;  for(int i=0;i<=25;i++) nd[0].ch[i]=-1;  for(int i=0;i<n;i++) extend(s[i]-'a');  for(int i=0;i<=tot;i++)    if (nd[i].step==n) {vis[i]=1;break;}  calc_s(0);}void output(int a){  int v=0;  while(a)  {    for(int i=0;i<=25;i++)  if (nd[v].ch[i]!=-1)  {    if (a>nd[nd[v].ch[i]].s) a-=nd[nd[v].ch[i]].s;else {v=nd[v].ch[i];printf("%c",i+'a');break;}  }a--;  }  if (q) printf("\n");}int main(){  scanf("%s",s);  n=strlen(s);  build();    scanf("%d",&q);  while(q--)  {    int a;scanf("%d",&a);output(a);  }    return 0;}


0 0
原创粉丝点击