码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P5284 [十二省联考2019]字符串问题

时间:2020-02-18 20:48:43      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:using   排序   print   cto   code   第一个   link   line   set   

Link
\(u\)支配\(v\)则连边\(A_u\rightarrow B_v\)
\(B_u\)\(A_v\)的前缀则连边\(B_u\rightarrow A_v\)
那么我们要求的就是这张图的点权最长路,拓扑排序+dp即可。
考虑如何优化建图。
先建出反串的SAM。
然后对于给定的子串\(s_{l,r}\),parent树上倍增找到它在SAM中的出现位置,然后把这个子串挂在SAM的这个点上。
注意到SAM上一个节点中的字符串在反串中互为后缀,因此它们在原串中互为前缀。
那么我们以结束位置为第一关键字,是否为\(A\)类子串(是为\(1\),不是为\(0\))为第二关键字对每个节点上的子串降序排序。
这样每一个\(B\)子串类子串要连向的就是这个节点上排在它后面的\(A\)类子串。
注意到我们要求的是最长路,因此只需要向后面第一个\(A\)类子串连边即可。

#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
namespace IO
{
    char ibuf[(1<<26)+1],*iS=ibuf;
    void In(){fread(ibuf,1,1<<26,stdin);}
    int read(){int x=0;while(!isdigit(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    void gets(char*s){while(isspace(*iS))++iS;while(islower(*iS))*s++=*iS++;*s='\0';}
}
using i64=long long;
using IO::read;
const int N=200007;
int cnt,tot,now,link[N*2],len[N*4],trans[N*2][26],id[N],fa[20][N*2],a[N],b[N],is[N*4],las[N*2],in[N*4];
char str[N];i64 dis[N*4];
std::vector<int>e[N*4],g[N*2];std::queue<int>q;
void clear()
{
    for(int i=0;i<20;++i) memset(fa[i]+1,0,cnt<<2);
    for(int i=1;i<=cnt;++i) g[i].clear();
    for(int i=1;i<=tot;++i) e[i].clear();
    memset(trans[1],0,cnt*104),memset(is+1,0,tot<<2),memset(in+1,0,tot<<2),memset(dis+1,0,tot<<3),cnt=now=1;
}
void extend(int c)
{
    int p=now,q;len[now=++cnt]=len[p]+1;
    for(;p&&!trans[p][c];p=link[p]) trans[p][c]=now;
    if(!p) return link[now]=1,void();
    if(len[q=trans[p][c]]==len[p]+1) return link[now]=q,void();
    len[++cnt]=len[p]+1,memcpy(trans[cnt],trans[q],104),link[cnt]=link[q],link[q]=link[now]=cnt;
    for(;p&&trans[p][c]==q;p=link[p]) trans[p][c]=cnt;
}
void getin(int flg)
{
    int l=read(),r=read()-l+1;l=id[l];
    for(int i=19;~i;--i) if(fa[i][l]&&len[fa[i][l]]>=r) l=fa[i][l];
    is[++tot]=flg,len[tot]=r,g[l].push_back(tot);
}
i64 toposort()
{
    i64 ans=0;
    for(int i=1;i<=tot;++i) if(!in[i]) q.push(i);
    for(int u;!q.empty();)
    {
    u=q.front(),q.pop(),ans=std::max(ans,dis[u]+len[u]);
    for(int v:e[u]) if(dis[v]=std::max(dis[v],dis[u]+len[u]),!--in[v]) q.push(v);
    }
    for(int i=1;i<=tot;++i) if(in[i]) return -1;
    return ans;
}
int main()
{
    IO::In();
    for(int T=read(),n,m,na,nb;T;--T)
    {
    clear(),IO::gets(str+1),n=strlen(str+1);
    for(int i=n;i;--i) extend(str[i]-'a'),id[i]=now;
    memcpy(fa[0]+1,link+1,cnt<<2),tot=cnt;
    for(int j=1;j<20;++j) for(int i=1;i<=cnt;++i) fa[j][i]=fa[j-1][fa[j-1][i]];
    na=read();for(int i=1;i<=na;++i) getin(1),a[i]=tot;
    nb=read();for(int i=1;i<=nb;++i) getin(0),b[i]=tot;
    for(int i=1;i<=cnt;++i) std::sort(g[i].begin(),g[i].end(),[](int i,int j){return len[i]<len[j]||(len[i]==len[j]&&is[i]<is[j]);});
    for(int i=1;i<=cnt;++i)
    {
        int u=i;
        for(int v:g[i]) if(e[u].push_back(v),++in[v],!is[v]) u=v;
        las[i]=u;
    }
    for(int i=2;i<=cnt;++i) e[las[link[i]]].push_back(i),++in[i];
    for(int i=1;i<=tot;++i) if(!is[i]) len[i]=0;
    m=read();for(int i=1,u,v;i<=m;++i) u=read(),v=read(),e[a[u]].push_back(b[v]),++in[b[v]];
    printf("%lld\n",toposort());
    }
}

Luogu P5284 [十二省联考2019]字符串问题

标签:using   排序   print   cto   code   第一个   link   line   set   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12327651.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!