zoukankan      html  css  js  c++  java
  • [NOI2018]你的名字

    description

    luogu
    给定长为(n)的文本串(S)(Q)个模式串(T),每次询问不在(S[l,r])中出现的(T)的本质不同子串个数。
    对于前(68\%)的数据有(l=1,r=|S|)

    solution

    我会(l=1,r=|S|)+单次询问!
    建出(S),(T)(SAM),算出两个串各自的本质不同子串个数;
    然后把(T)(S)(SAM)上跑一遍,(dfs)得到每个节点的最长匹配长度,最后把每个节点相加就可以得到(T)(S)中出现的本质不同子串个数,减掉即可。
    这样的时间复杂度很高,为(O(Q(|S|+|T_i|))),只能通过原题(28\%)的数据。

    (S)(SAM)上做肯定会(TLE),我们看到(T)(SAM),对于每个节点,考虑其和(S)重复的部分,这一定是(T)的某个前缀(i)的一段长度连续的后缀。
    进而我们知道只要前缀(i)的某一个后缀与(S)重复,那么前缀(i)小于这个后缀长度的所有后缀一定也和(S)重复。
    那么对于(T)(SAM)中的每一个节点,我们要考虑其对答案的贡献,假设这个节点(endpos)集合的其中一个为(i),那么只要根据前缀(i)的最长匹配长度(len_i)进行判断即可。
    数组(len[])的长度为(|T|),这个我们可以通过之前在(S)(SAM)上跑一遍的方法得到。
    于是总复杂度降为了(O(|S|+sum|T|)),可以通过原题(68\%)的数据。

    最后考虑如果(S)变成了(S[l,r])应该如何去做。
    直接按照(l=1,r=|S|)去做出现的唯一问题就是(len_i)不一定是正确的。
    也就是说现在我们需要正确得到在(S[l,r])上进行匹配的(len_i)
    考虑线段树合并维护每个节点的(endpos)集合,那么我们在(SAM)上转移时,只要满足转移后的节点存在合法的(endpos)即可转移。
    存在合法的(endpos)指对于转移后的(len_i),有(T)的前缀(i)的长为(len_i)的后缀在(S[l,r])内。
    但我们无法提前知道这个(len_i),因此需要通过二分(或者暴(for))来得到最终答案。

    code

    震惊!NOI2018D1T3代码竟不到100行

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e6+10;
    inline ll read(){
      ll data=0,w=1;char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
      if(ch=='-')w=-1,ch=getchar();
      while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
      return data*w;
    }
    int n,m,Q,res[N];char S[N],T[N];
    int head[N],nxt[N],to[N],cnt;
    inline void add(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}
    int rt[N],s[2][100*N],tot;
    #define mid ((l+r)>>1)
    void insert(int &i,int l,int r,int p){
      i=++tot;s[0][i]=s[1][i]=0;if(l==r)return;
      p<=mid?insert(s[0][i],l,mid,p):insert(s[1][i],mid+1,r,p);
    }
    int merge(int a,int b){
      if(!a||!b)return a|b;int c=++tot;
      s[0][c]=merge(s[0][a],s[0][b]);s[1][c]=merge(s[1][a],s[1][b]);return c;
    }
    int query(int i,int l,int r,int x,int y){
      if(!i||y<l||r<x||x>y)return 0;if(x<=l&&r<=y)return 1;
      return query(s[0][i],l,mid,x,y)|query(s[1][i],mid+1,r,x,y);
    }
    struct SAM{
      int lst,tot,tr[N][26],len[N],fa[N],pos[N],cntp;
      inline void clear(){
        for(int i=1;i<=tot;i++)
          len[i]=fa[i]=pos[i]=0,memset(tr[i],0,sizeof(tr[i]));
        lst=tot=1;cntp=0;
      }
      inline void extend(int c){
        int cur=++tot,u=lst;len[cur]=len[lst]+1;
        while(u&&!tr[u][c])tr[u][c]=cur,u=fa[u];
        if(!u)fa[cur]=1;
        else{
          int v=tr[u][c];
          if(len[v]==len[u]+1)fa[cur]=v;
          else{
        int clone=++tot;len[clone]=len[u]+1;pos[clone]=pos[v];
        memcpy(tr[clone],tr[v],sizeof(tr[clone]));
        fa[clone]=fa[v];fa[cur]=fa[v]=clone;
        while(u&&tr[u][c]==v)tr[u][c]=clone,u=fa[u];
          }
        }
        lst=cur;pos[cur]=++cntp;
      }
      void dfs(int u){
        if(pos[u])insert(rt[u],1,n,pos[u]);
        for(int i=head[u],v;i;i=nxt[i])dfs(v=to[i]),rt[u]=merge(rt[u],rt[v]);
      }
      inline void work(){for(int i=2;i<=tot;i++)add(fa[i],i);dfs(1);}
      inline ll calc(int *res){
        ll ans=0;
        for(int i=1;i<=tot;i++)ans+=max(len[i]-max(len[fa[i]],res[pos[i]]),0);
        return ans;
      }
      inline void run(char *T,int m,int x,int y,int *res){
        for(int i=1,c,u=1,l=0,v,L,R,M,AS;i<=m;i++){
          c=T[i]-'a';
          while(1){
    	if(!u){u=1;l=0;break;}
    	if(!tr[u][c]){u=fa[u];l=min(l,len[u]);continue;}
    	v=tr[u][c];L=len[fa[v]]+1;R=l+1;AS=-1;
    	while(L<=R){
    	  M=(L+R)>>1;
    	  if(query(rt[v],1,n,x+M-1,y))
    	    AS=M,L=M+1;
    	  else R=M-1;
    	}
    	if(AS==-1){u=fa[u];l=min(l,len[u]);}
    	else{u=v;l=AS;break;}
          } 
          res[i]=l;
        }
      }
    }samS,samT;
    int main()
    {
      scanf("%s",S+1);n=strlen(S+1);samS.clear();samT.clear();
      for(int i=1;i<=n;i++)samS.extend(S[i]-'a');samS.work();Q=read();
      while(Q--){
        scanf("%s",T+1);m=strlen(T+1);samT.clear();int l=read(),r=read();
        for(int i=1;i<=m;i++)samT.extend(T[i]-'a');
        samS.run(T,m,l,r,res);printf("%lld
    ",samT.calc(res));
      }
      return 0;
    }
    
  • 相关阅读:
    hdu1180奇怪的楼梯……bfs迷阵……wa该16二级,我太渣滓
    Android新闻案例clientserver达到,完全自己的新闻节目平台
    xcode代码统计行
    linux每个路由表的系统研究
    Oracle 学习笔记 18 -- 存储函数和存储过程(PL/SQL子程序)
    《网络编程》ioctl 操作
    Android framework召回(3)binder使用和IBinder BpRefbase IInterface INTERFACE 之间的关系
    使用关节型
    leetcode 218: The Skyline Problem
    删除依赖命令
  • 原文地址:https://www.cnblogs.com/cjfdf/p/10263380.html
Copyright © 2011-2022 走看看