【纪中集训2019.3.19】原样输出

博客围绕给出的n个字符串的两种询问展开,询问一是求每个串连续子串拼接的不同串数量,询问二是回答询问一并输出所有子串。题解采用对每个串建后缀自动机,补上空节点,用dp统计答案、dfs输出方案,复杂度为O(4*n)。

题意

描述:

​ 给出\(n\)个字符串,你可能会回答两种询问中的一个:

\(k=0\):其中每个串的连续子串拼接起来共有多少个不同的串;

\(k=1\):回答\(k=0\),并且输出所有的子串;

范围:

​ 保证输入不超过\(1 \ M\) ,输出不超过\(200 \ M\)

题解:

  • 对每个串建后缀自动机;

  • 把每个后缀自动机的空节点仿照子序列自动机那样补上;

  • \(dp\)统计答案,\(dfs\)输出方案;

  • 复杂度是\(O(4*n)\)的;

    #include<bits/stdc++.h>
    #define ll long long 
    using namespace std;
    const int N=1<<21,mod=1e9+7; 
    int n,k,nxt[4],l[N];
    int cnt,rt[N],len[N],pa[N],ch[N][4],lst,now,L[N],d[N];
    int st[N],top,c[N],f[N],ans;
    queue<int>q;
    char s[N],mp[4]={'A','C','G','T'};
    int get(char x){return x=='A'?0:x=='C'?1:x=='G'?2:3;}
    void extend(int x){
      int p=lst,np=++cnt;lst=np;
      len[np]=len[p]+1;
      while(p&&!ch[p][x])ch[p][x]=np,p=pa[p];
      if(!ch[p][x]){pa[np]=now;return;}
      int q=ch[p][x];
      if(len[q]==len[p]+1){pa[np]=q;return;}
      int nq=++cnt;
      len[nq]=len[p]+1;
      memcpy(ch[nq],ch[q],sizeof(ch[q]));
      pa[nq]=pa[q];pa[q]=pa[np]=nq;
      while(p&&ch[p][x]==q)ch[p][x]=nq,p=pa[p];
    }
    void dfs(int u){
      for(int i=1;i<=top;++i)putchar(mp[st[i]]);
      putchar('\n');ans++;
      for(int i=0;i<4;++i)if(ch[u][i]){
          st[++top]=i;
          dfs(ch[u][i]); 
          --top;
      }
    }
    int main(){
      freopen("copy.in","r",stdin);
      freopen("copy.out","w",stdout);
      scanf("%d",&n);
      for(int i=1;i<=n;++i){
          scanf("%s",s+1);
          l[i]=strlen(s+1);
          rt[i]=now=lst=++cnt;
          for(int j=1;j<=l[i];++j)extend(get(s[j]));
      }
      scanf("%d",&k);
      for(int i=n;i;--i){
          now=rt[i];
          for(int j=now;j<rt[i+1];++j)
          for(int k=0;k<4;++k)if(!ch[j][k])ch[j][k]=nxt[k];
          for(int j=0;j<4;++j)if(ch[now][j])nxt[j]=ch[now][j];
      }
      if(k==1)dfs(1);
      else {
          for(int i=1;i<=cnt;++i)
          for(int j=0;j<4;++j)d[ch[i][j]]++;
          for(int i=1;i<=cnt;++i)if(!d[i])q.push(i),st[++top]=i;
          while(!q.empty()){
              int x=q.front();q.pop();
              for(int i=0;i<4;++i)if(!--d[ch[x][i]])q.push(ch[x][i]),st[++top]=ch[x][i];
          }
          for(int i=cnt,x;i;--i){
              f[x=st[i]]++;
              for(int j=0;j<4;++j)if(ch[x][j]){f[x]+=f[ch[x][j]];if(f[x]>=mod)f[x]-=mod;}
          }
          ans=f[1];
      }
      printf("%d\n",ans%mod);
      return 0;
    }

转载于:https://www.cnblogs.com/Paul-Guderian/p/10560514.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值