zoukankan      html  css  js  c++  java
  • [NOI2018]你的名字(SAM+线段树合并)

    考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献。

    T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长公共子串的长度。

    从1到length(T)扫一遍,SAM基本操作求出每个前缀与S的最长公共子串。

    答案为$sum_{i=1}^{cnt}max(0,mx[x]-max(mx[fa[x]],len[tag[x]]))$,其中tag[x]是x所代表的子串在T中的第一个出现位置,len[i]为T的前缀i与S的最长公共子串。

    再考虑l,r任意的情况,唯一区别在于求某前缀与S的最长公共子串时,需要满足[l,r]的限制。

    即若想判断当前公共子串长是否可以为len,那么当前走到的点的parent树的子树内必须存在一个串的Right在[l+len,r]中。

    “某子树内是否存在[l+len,r]中的值”显然可以用主席树做,或可持久化线段树合并。

    下面大概证一下线段树合并的时空复杂度:

    1.由于每次合并时,若发现x与y的当前子树有一个为空则退出。这种情况每个节点最多会出现一次(合并之后这个位置就不再为空了),故这部分复杂度为$O(nlog n)$。

    2.除去情况一,则x与y合并的复杂度为两个树的重合节点个数。最坏情况发生在依次将一个个只有一个节点的树合并进来,这部分显然仍然是$O(nlog n)$的。

    3.不可持久化的线段树合并并不产生新节点,故空间复杂度为初始插入n个点的复杂度:$O(nlog n)$

    4.可持久化的线段树,根据算法流程容易发现时空复杂度同阶,于是复杂度也为$O(nlog n)$

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define lson ls[x],L,mid
     5 #define rson rs[x],mid+1,R
     6 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     7 typedef long long ll;
     8 using namespace std;
     9 
    10 const int N=2000010,M=40000010;
    11 char s[N],t[N];
    12 int n,m,nd,Q,l,r,len[N],rt[N],id[N],ls[M],rs[M];
    13 
    14 struct SAM{
    15     int nd,lst,mx[N],fa[N],pos[N],son[N][27];
    16     
    17     void init(){
    18         nd=1; lst=1;
    19         rep(i,0,m*2) fa[i]=mx[i]=0;
    20         rep(i,0,m*2) rep(j,0,26) son[i][j]=0;
    21     }
    22     
    23     void ext(int c,int x){
    24         int p=lst,np=lst=++nd; pos[np]=x; mx[np]=mx[p]+1;
    25         while (p && !son[p][c]) son[p][c]=np,p=fa[p];
    26         if (!p) fa[np]=1;
    27         else{
    28             int q=son[p][c];
    29             if (mx[q]==mx[p]+1) fa[np]=q;
    30             else{
    31                 int nq=++nd; pos[nq]=pos[q]; mx[nq]=mx[p]+1;
    32                 memcpy(son[nq],son[q],sizeof(son[q]));
    33                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
    34                 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
    35             }
    36         }
    37     }
    38     
    39 }S,T;
    40 
    41 bool cmp(int a,int b){ return S.mx[a]<S.mx[b]; }
    42 
    43 void ins(int &x,int L,int R,int k){
    44     if (!x) x=++nd;
    45     if (L==R) return;
    46     int mid=(L+R)>>1;
    47     if (k<=mid) ins(lson,k); else ins(rson,k);
    48 }
    49 
    50 bool que(int x,int L,int R,int l,int r){
    51     if (!x) return 0;
    52     if (L==l && r==R) return 1;
    53     int mid=(L+R)>>1;
    54     if (r<=mid) return que(lson,l,r);
    55     else if (l>mid) return que(rson,l,r);
    56         else return que(lson,l,mid)|que(rson,mid+1,r);
    57 }
    58 
    59 int merge(int x,int y){
    60     if (!x || !y) return x|y;
    61     int now=++nd;
    62     ls[now]=merge(ls[x],ls[y]);
    63     rs[now]=merge(rs[x],rs[y]);
    64     return now;
    65 }
    66 
    67 int main(){
    68     freopen("name.in","r",stdin);
    69     freopen("name.out","w",stdout);
    70     scanf("%s",s+1); n=strlen(s+1);
    71     S.init(); rep(i,1,n) S.ext(s[i]-'a',i),ins(rt[S.lst],1,n,i);
    72     rep(i,1,S.nd) id[i]=i;
    73     sort(id+1,id+S.nd+1,cmp);
    74     for (int i=S.nd; i>1; i--) rt[S.fa[id[i]]]=merge(rt[S.fa[id[i]]],rt[id[i]]);
    75     for (scanf("%d",&Q); Q--; ){
    76         scanf("%s%d%d",t+1,&l,&r); m=strlen(t+1);
    77         int now=0,x=1;
    78         T.init(); rep(i,1,m) T.ext(t[i]-'a',i);
    79         rep(i,1,m){
    80             int c=t[i]-'a';
    81             while (1){
    82                 if (!que(rt[S.son[x][c]],1,n,l+now,r)){
    83                     if (!now) break; now--;
    84                     if (now==S.mx[S.fa[x]]) x=S.fa[x];
    85                 }else{ now++; x=S.son[x][c]; break; }
    86             }
    87             len[i]=now;
    88         }
    89         ll ans=0;
    90         rep(i,2,T.nd) ans+=max(0,T.mx[i]-max(T.mx[T.fa[i]],len[T.pos[i]]));
    91         printf("%lld
    ",ans);
    92     }
    93     return 0;
    94 }
  • 相关阅读:
    [Swift]LeetCode374. 猜数字大小 | Guess Number Higher or Lower
    [Swift]LeetCode371. 两整数之和 | Sum of Two Integers
    [Swift]LeetCode367. 有效的完全平方数 | Valid Perfect Square
    [Swift]LeetCode350. 两个数组的交集 II | Intersection of Two Arrays II
    [Swift]LeetCode349. 两个数组的交集 | Intersection of Two Arrays
    [Swift实际操作]七、常见概念-(9)使用定时组件Timer执行定时任务
    [Swift实际操作]七、常见概念-(8)日历Calendar和时区TimerZone
    浅谈广告交易系统
    浅谈广告交易系统
    6种排序算法的简洁实现:冒泡、选择、插入、归并、快速、堆
  • 原文地址:https://www.cnblogs.com/HocRiser/p/10229780.html
Copyright © 2011-2022 走看看