NOI 2018 你的名字 (后缀自动机+线段树合并)

题目大意:略

令$ION2017=S,ION2018=T$

对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$

由于$T$必须在$[l,r]$上匹配,设现在能匹配的长度为$len$,在后缀自动机的$x$点,添加一个字符$c$,则$trs[x][c]$的$right$集合中必须包含$endposin[l+len,r]$,这个操作可以用线段树合并实现

否则$len$就要缩短,直到$len$缩短到$dep[pre_{x}]$,$len$如果再缩短就会无意义,跳$pre$,重复上述操作,直到$len$小于0

求出$f_{i}$以后,我们还要对$T$去重,不去重就会爆零

后缀自动机可以看成一个合并后缀的前缀树,相同的后缀是一定会被合并到一起的,每次都跳到能表示$T_{i-f_{i}+1,i}$的位置

由于每次表示的串不一定能取满这个节点,所以答案是$sum_{2}^{tot} max(0,dep_{x}-max(dep_{pre_{x}},max(f_{k})))$

仔细一想就能明白这个式子的含义了,考虑不合法的情况,要么$x$表示的串全都能被取,要么只能取到最大的经过它的$f_{i}$,取个补集即可

$upd$:这道题卡$AK$的方式也很细节。

我们把$T$放在$S$上匹配,求以每个位置为后缀,在$S$中的最长匹配长度$Len$时

$Len$应该不断$-1$地缩减,直到缩减到$dep[fa]$,再跳到父节点$fa$,而不是直接缩减到$dep[fa]$

因为我们用线段树合并去检验当前答案$len$是否合法,能取的区间是$[l+len,r]$

如果$len$不合法,说明这个节点能表示的,长度为$len$的串不合法。

如果这个节点还能表示其他长度小于$len$的串,就应该依次检查这些串是否都合法。

代码体现就像我上面说的那样,$len$依次$-1$即可。

时间复杂度依然不变,$len$依次$-1$,而我们从左往右遍历整个$T$串,类似于一个双指针,时间复杂度是$O(n)$级别,算上线段树合并就是$O(nlogn)$

  1 #include <cmath>
  2 #include <vector>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <algorithm>
  6 #define N1 500010
  7 #define S1 (N1<<1)
  8 #define ll long long
  9 #define uint unsigned int
 10 #define rint register int 
 11 #define dd double
 12 #define il inline 
 13 #define inf 0x3f3f3f3f
 14 #define idx(X) (X-'a')
 15 using namespace std;
 16 
 17 int gint()
 18 {
 19     int ret=0,fh=1;char c=getchar();
 20     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 21     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 22     return ret*fh;
 23 }
 24 int n,len,cnt;
 25 /*struct Edge{
 26 int head[S1],to[S1],nxt[S1],cte;
 27 void ae(int u,int v){
 28     cte++;to[cte]=v,nxt[cte]=head[u],head[u]=cte;}
 29 }E;*/
 30 struct SEG{
 31 int sum[S1*60],ls[S1*60],rs[S1*60],root[S1],tot;
 32 int merge(int r1,int r2)
 33 {
 34     if(!r1||!r2) return r1+r2;
 35     int nx=++tot;
 36     sum[nx]=sum[r1]+sum[r2];
 37     ls[nx]=merge(ls[r1],ls[r2]);
 38     rs[nx]=merge(rs[r1],rs[r2]);
 39     return nx;
 40 }
 41 void update(int x,int l,int r,int &rt)
 42 {
 43     if(!rt) rt=++tot;
 44     sum[rt]++;
 45     if(l==r) return;
 46     int mid=(l+r)>>1;
 47     if(x<=mid) update(x,l,mid,ls[rt]);
 48     else update(x,mid+1,r,rs[rt]);
 49 }
 50 ll query(int L,int R,int l,int r,int rt)
 51 {
 52     if(!rt||L>R) return 0;
 53     if(L<=l&&r<=R) return sum[rt];
 54     int mid=(l+r)>>1;ll ans=0;
 55     if(L<=mid) ans+=query(L,R,l,mid,ls[rt]);
 56     if(R>mid) ans+=query(L,R,mid+1,r,rs[rt]);
 57     return ans;
 58 }
 59 }s;
 60 
 61 char str[S1];
 62 int L[S1];//sz[S1],psz[S1];
 63 namespace S{
 64 int trs[S1][26],pre[S1],dep[S1],la,tot;
 65 void init(){tot=la=1;}
 66 void insert(int c,int id)
 67 {
 68     int p=la,np=++tot,q,nq;la=np;
 69     dep[np]=dep[p]+1;
 70     for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
 71     s.update(id,1,n,s.root[np]);
 72     if(!p) {pre[np]=1;return;}
 73     q=trs[p][c];
 74     if(dep[q]==dep[p]+1) pre[np]=q;
 75     else{
 76         pre[nq=++tot]=pre[q];
 77         pre[q]=pre[np]=nq;
 78         dep[nq]=dep[p]+1;
 79         memcpy(trs[nq],trs[q],sizeof(trs[q]));
 80         for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
 81     }
 82 }
 83 int hs[S1],que[S1];
 84 void topo()
 85 {
 86     int i,x;
 87     for(i=2;i<=tot;i++) hs[dep[i]]++;
 88     for(i=1;i<=tot;i++) hs[i]+=hs[i-1];
 89     for(i=2;i<=tot;i++) que[hs[dep[i]]--]=i;
 90     for(i=tot-1;i;i--)
 91     {
 92         x=que[i];
 93         s.root[pre[x]]=s.merge(s.root[x],s.root[pre[x]]);
 94     }
 95 }
 96 inline int Min(int X,int Y){return X<Y?X:Y;}
 97 void find(int l,int r)
 98 {
 99     int i,x=1,c,mi;
100     for(i=1;i<=len;i++)
101     {
102         c=idx(str[i]);
103         L[i]=L[i-1];
104         while(1)
105         {
106             if(!trs[x][c]||!s.query(l+L[i],r,1,n,s.root[trs[x][c]])) {L[i]--;}
107             else {L[i]++;x=trs[x][c];break;}
108             if(L[i]==-1) {L[i]=0;break;}
109             else if(L[i]==dep[pre[x]]) x=pre[x];
110         } 
111             /*for(;pre[x]&&(!trs[x][c]||!s.query(l+Min(L[i-1],dep[x]),r,1,n,s.root[trs[x][c]]));x=pre[x]);
112             if(!trs[x][c]||){
113                 L[i]=0;
114             }else{
115                 L[i]=min(L[i-1]+1,dep[x]+1);
116                 x=trs[x][c];
117             }*/
118     }
119 }
120 };
121 namespace T{
122 int trs[S1][26],pre[S1],dep[S1],la,tot;
123 void init(){tot=la=1;}
124 void insert(int c)
125 {
126     int p=la,np=++tot,q,nq;la=np;
127     dep[np]=dep[p]+1;
128     for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
129     if(!p) {pre[np]=1;return;}
130     q=trs[p][c];
131     if(dep[q]==dep[p]+1) pre[np]=q;
132     else{
133         pre[nq=++tot]=pre[q];
134         pre[q]=pre[np]=nq;
135         dep[nq]=dep[p]+1;
136         memcpy(trs[nq],trs[q],sizeof(trs[q]));
137         for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
138     }
139 }
140 int ma[S1],hs[S1],que[S1];
141 ll uniq()
142 {
143     int i,x,c; ll ans=0;
144     for(i=2;i<=tot;i++) hs[dep[i]]++;
145     for(i=1;i<=tot;i++) hs[i]+=hs[i-1];
146     for(i=2;i<=tot;i++) que[hs[dep[i]]--]=i;
147     x=1;
148     for(i=1;i<=len;i++)
149     {
150         c=idx(str[i]);
151         for(;pre[x]&&dep[pre[x]]>=L[i]-1;x=pre[x]);
152         x=trs[x][c];
153         ma[x]=max(ma[x],L[i]);
154     }
155     for(i=tot-1;i;i--)
156     {
157         x=que[i];
158         if(ma[x]) ma[pre[x]]=dep[pre[x]];
159     }
160     for(x=2;x<=tot;x++)
161         ans+=max(0,dep[x]-max(dep[pre[x]],ma[x]));
162     return ans;
163 }
164 void clr()
165 {
166     tot++;
167     memset(hs,0,tot*4),memset(ma,0,tot*4);
168     memset(que,0,tot*4),memset(pre,0,tot*4);
169     memset(dep,0,tot*4),memset(trs,0,tot*4*26);
170     memset(L,0,(len+1)*4);
171     tot=la=1;
172 }
173 };
174 
175 int main()
176 {
177     freopen("t2.in","r",stdin);
178     //freopen("a.out","w",stdout);
179     int i,j,Q,l,r;ll ans1,ans2;
180     S::init();
181     scanf("%s",str+1);n=strlen(str+1);
182     for(i=1;i<=n;i++) S::insert(idx(str[i]),i);
183     S::topo();
184     scanf("%d",&Q);
185     for(i=1;i<=Q;i++)
186     {
187         scanf("%s",str+1);
188         scanf("%d%d",&l,&r);
189         len=strlen(str+1);
190         T::clr();
191         for(j=1;j<=len;j++) T::insert(idx(str[j]));
192         S::find(l,r);
193         printf("%lld
",T::uniq());
194     }
195     return 0;
196 }
原文地址:https://www.cnblogs.com/guapisolo/p/10137749.html