• [bzoj5417]你的名字


    先考虑l=1,r=|s|的部分分,需要求出t每一个前缀的不是s子串的最长后缀,记作pp[k],有以下限制:
    1.pp[pos[k]]<len(pos[k]表示k的某一个结束位置),因为不能被匹配
    2.len[fa[k]]<len<=len[k],因为这个点上本来就只有这些串
    由此得到答案为sigma(max(0,len[j]-max(len[fa[j]],pp[pos[j]])))(要对0取max)
    维护出s从li到ri的后缀自动机,但好像不太容易。不妨换个角度,即要求其的right集合存在一个值在[li,ri]之间
    也就是说在计算s的SAM同时,求出每一个节点的right集合
    维护线段树,线段树下标表示位置,值表示是否是right集合。那么SAM上的父亲就是所有儿子right集合的并,也就是线段树合并
    (注意:因为要保留参与合并的线段树,所以要新建节点)
    同时线段树还能很方便的判断区间中有没有1,求区间和即可
    但是要注意,此时不能直接跳fa,而是要一个一个减长度(因为可能无法匹配该节点最长串,但能匹配较短的串)
      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 2000005
      4 #define mid (l+r>>1)
      5 struct ji{
      6     int nex,to;
      7 }edge[N];
      8 int V,x,last,fa[N],pos[N],len[N],ch[N][26];
      9 int VV,E,n,m,a,b,r[N],head[N],pp[N],ls[N*16],rs[N*16];
     10 long long ans;
     11 char s[N],t[N];
     12 int New(){
     13     fa[++V]=0;
     14     len[V]=pos[V]=0;
     15     memset(ch[V],0,sizeof(ch[V]));
     16     return V;
     17 }
     18 void add_edge(int x,int y){
     19     edge[E].nex=head[x];
     20     edge[E].to=y;
     21     head[x]=E++;
     22 }
     23 void add(int c,int id){
     24     int p=last,np=last=New();
     25     len[np]=len[p]+1;
     26     pos[V]=id;
     27     for(;(p)&&(!ch[p][c]);p=fa[p])ch[p][c]=np;
     28     if (!p)fa[np]=x;
     29     else{
     30         int q=ch[p][c];
     31         if (len[q]==len[p]+1)fa[np]=q;
     32         else{
     33             int nq=New();
     34             pos[nq]=pos[q];
     35             len[nq]=len[p]+1;
     36             memcpy(ch[nq],ch[q],sizeof(ch[q]));
     37             fa[nq]=fa[q];
     38             fa[q]=fa[np]=nq;
     39             for(;(p)&&(ch[p][c]==q);p=fa[p])ch[p][c]=nq;
     40         }
     41     }
     42 }
     43 void update(int &k,int l,int r,int x){
     44     k=++VV;
     45     if (l==r)return;
     46     if (x<=mid)update(ls[k],l,mid,x);
     47     else update(rs[k],mid+1,r,x);
     48 }
     49 int query(int k,int l,int r,int x,int y){
     50     if ((!k)||(l>y)||(x>r))return 0;
     51     if ((x<=l)&&(r<=y))return 1;
     52     return query(ls[k],l,mid,x,y)|query(rs[k],mid+1,r,x,y);
     53 }
     54 void merge(int &k1,int k2){
     55     if (k1*k2==0){
     56         k1+=k2;
     57         return;
     58     }
     59     ls[++VV]=ls[k1];
     60     rs[VV]=rs[k1];
     61     k1=VV;
     62     merge(ls[k1],ls[k2]);
     63     merge(rs[k1],rs[k2]);
     64 }
     65 void dfs(int k){
     66     if (pos[k])update(r[k],1,n,pos[k]);
     67     for(int i=head[k];i!=-1;i=edge[i].nex){
     68         dfs(edge[i].to);
     69         merge(r[k],r[edge[i].to]);
     70     }
     71 }
     72 int main(){
     73     scanf("%s%d",s,&m);
     74     len[0]=-1;
     75     n=strlen(s);
     76     x=last=New();
     77     for(int i=0;s[i];i++)add(s[i]-'a',i+1);
     78     memset(head,-1,sizeof(head));
     79     for(int i=2;i<=V;i++)add_edge(fa[i],i);
     80     dfs(1);
     81     x=New();
     82     for(int i=1;i<=m;i++){
     83         scanf("%s%d%d",t,&a,&b);
     84         V=x-1;
     85         last=New();
     86         for(int j=0,k=1,l=0;t[j];pp[j++]=++l){
     87             int c=t[j]-'a';
     88             add(c,j);
     89             while (!query(r[ch[k][c]],1,n,a+l,b)){
     90                 if (--l<0)break;
     91                 if (l==len[fa[k]])k=fa[k];
     92             }
     93             if (l<0)k=1;
     94             else k=ch[k][c];
     95         }
     96         ans=0;
     97         for(int j=x;j<=V;j++)ans+=max(0,len[j]-max(len[fa[j]],pp[pos[j]]));
     98         printf("%lld\n",ans);
     99     }
    100 }
    View Code
  • 相关阅读:
    [Java]基础知识复习:例外的在继承中的机制
    2005年7月28日,终于结束了。
    从不知道到知道,从没有到有,是一个质的进步。
    正确的心态、积极的态度、坚定的信心、愉快的心情
    今天终于见到了她。
    textarea自增高(无滚动条)纯js实现
    带,号字符串转成表的函数操作
    MAK密钥集锦
    用户注册信息验证类库
    C#将文档(Word\ Excel\ PowerPoint\ Visio\ text\ XML\ RTF\ CSV )转成Pdf
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/11516907.html
Copyright © 2020-2023  润新知