zoukankan      html  css  js  c++  java
  • 【SPOJ7258】Lexicographical Substring Search-后缀自动机+拓补序递推

    测试地址: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("
    ");
    }
    
    int main()
    {
      scanf("%s",s);
      n=strlen(s);
      build();
      
      scanf("%d",&q);
      while(q--)
      {
        int a;
    	scanf("%d",&a);
    	output(a);
      }
      
      return 0;
    }
    


  • 相关阅读:
    [Cordova] 无法编译Visual Studio项目里Plugin副本的Native Code
    [Cordova] 无法显示Alert视窗
    [ASP.NET MVC] 使用CLK.AspNet.Identity提供依权限显示选单项目的功能
    [Tool] SourceTree初始化GitFlow遇到错误(git command not found)的解决方案
    [.NET] 使用Json.NET提供依赖注入功能(Dependence Injection)
    [AngularJS] 使用AngularCSS动态加载CSS
    [AngularJS] 使用AngularAMD动态加载Service
    [AngularJS] 使用AngularAMD动态加载Controller
    [Tool] Chrome内的本地网页,使用XMLHttpRequest读取本地档案
    [Android] WebView内的本地网页,使用XMLHttpRequest读取本地档案
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793755.html
Copyright © 2011-2022 走看看