[十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增
生活随笔
收集整理的這篇文章主要介紹了
[十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目描述:
給定一個長串 $S$,給定若干 $S$ 的子串 $a_{i}$, $b_{i}$,再給出一些 $a$ 串和 $b$ 串的支配關系.
構造一個長度最長的字符串,使得:
字符串只由 $a_{i}$ 組成.
當且僅當 $a_{i}$ 所支配的一個串 $b_{i}$ 為 $a_{j}$ 的前綴,才可將 $a_{j}$ 連到 $a_{i}$ 后面.
首先,對于求前綴,我們可以對 $S$ 的反串建立后綴自動機,這樣每個 $f_{i}$ 將會轉移到當前串的一個前綴,而不是后綴.
第一步,先將所有的 $a$串和$b$串都定位到后綴自動機上.
對于這一步,我們使用倍增算法.
第二部,我們將支配邊進行連邊(即再后綴樹中對應的點和點).
順著后綴樹中的邊和支配邊進行轉移即可.
細節部分看一下代碼.
?
Code:
#include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <vector> #define setIO(s) freopen(s".in","r",stdin)// ,freopen(s".out","w",stdout) #define maxn 1500001 using namespace std; char str[maxn]; int debug; int length,na,nb,m; namespace SAM{#define N 29 #define ll long long #define L l=length-l+1#define R r=length-r+1 int last,tot; vector<int>ed[maxn]; int la[maxn],ra[maxn],h[maxn]; int ch[maxn][N],dis[maxn]; int f[maxn],trace[maxn],mk[maxn],pos[maxn]; int F[24][maxn]; int head[maxn],to[maxn],nex[maxn],edges; int ge[maxn]; int vis[maxn]; int trc; queue<int>Q; int du[maxn]; long long DP[maxn]; int idx[maxn]; int cmp(int i,int j){ if(h[i]==h[j]) return i>j; return h[i]<h[j]; }void add(int u,int v){nex[++edges] = head[u],head[u]=edges,to[edges]=v; mk[edges]=0; //if(!debug) printf("%d %d\n",u,v); }//SAM of S void ins(int c,int i){ int np = ++tot, p = last; last = np; dis[np] = i; while(p && !ch[p][c]) ch[p][c] = np, p = f[p]; if(!p) f[np] = 1;else { int q = ch[p][c]; if(dis[q] == dis[p] + 1) f[np] = q;else {int nq = ++tot; dis[nq] = dis[p] + 1; memcpy(ch[nq],ch[q],sizeof(ch[q])); f[nq] = f[q], f[np] = f[q] = nq; while(p && ch[p][c] == q) ch[p][c] = nq,p = f[p]; }}trace[i] = np; } void DFS(int u){ F[0][u]=f[u];for(int i=1;i<24;++i) F[i][u]=F[i-1][F[i-1][u]]; int sz=ed[u].size(); for(int i=0;i<sz;++i) DFS(ed[u][i]); ed[u].clear(); } void build(){int l,r;scanf("%d",&na);for(int i=1;i<=na;++i){scanf("%d%d",&l,&r); L;R; swap(l,r); la[i]=l,ra[i]=r,h[i]=r-l+1; } scanf("%d",&nb); for(int i=1;i<=nb;++i) {scanf("%d%d",&l,&r); L;R; swap(l,r); la[i+na]=l,ra[i+na]=r,h[i+na]=r-l+1; }for(int i=2;i<=tot;++i) ed[f[i]].push_back(i); trc=tot; DFS(1); for(int i=1;i<=na+nb;++i) {int p=trace[ra[i]]; for(int j=23;j>=0;--j) if(dis[F[j][p]] >= h[i]) p=F[j][p]; if(dis[p]==h[i]) pos[i]=p; else ed[p].push_back(i); }}void sol(){int nn=tot; for(int i=2;i<=nn;++i){ if(ed[i].size()==0){ add(f[i],i); continue; }int p=0; int sz=ed[i].size(); for(int j=0;j<sz;++j) ge[++p]=ed[i][j]; // sort(ge+1,ge+1+p,cmp); //將該點對應字符串按長度排序 int lst=f[i]; for(int j=1;j<=p;++j) { add(lst,++tot); dis[tot] = h[ge[j]]; pos[ge[j]]=tot; lst=tot; }add(pos[ge[p]],i); ed[i].clear(); }}void get(){int a,b; scanf("%d%d",&a,&b); add(pos[a],pos[b+na]); mk[edges]=1; } void solve(){Q.push(1); du[1]=0; long long ans=0; for(int i=1;i<=edges;++i) ++du[to[i]]; while(!Q.empty()){int u=Q.front(); Q.pop(); for(int i=head[u];i;i=nex[i]){--du[to[i]]; long long ed=DP[u]+dis[to[i]]-(mk[i]?0:dis[u]); DP[to[i]]=max(DP[to[i]],ed); if(du[to[i]]==0) Q.push(to[i]); }} for(int i=1;i<=tot;++i) if(du[i]!=0) {printf("-1\n"); return; }for(int i=1;i<=na;++i) ans=max(ans,DP[pos[i]]); printf("%lld\n",ans); }void init(){for(int i=1;i<=tot;++i) DP[i]=0; for(int i=1;i<=tot;++i) dis[i]=0; for(int i=1;i<=edges;++i) mk[i]=0; for(int i=1;i<=tot;++i) head[i]=0; for(int i=0;i<=tot;++i) du[i]=0; for(int i=1;i<=trc;++i) for(int j=0;j<=22;++j) F[j][i]=0; for(int i=1;i<=trc;++i) memset(ch[i],0,sizeof(ch[i])); last=tot=1,edges=0; } }; int main(){//setIO("string9"); int T; scanf("%d",&T); while(T--){ debug=T; SAM::last=SAM::tot=1; scanf("%s",str+1),length=strlen(str+1); for(int i=length;i;--i) SAM::ins(str[i]-'a',length-i+1); SAM::trc=SAM::tot; SAM::build(); SAM::sol(); scanf("%d",&m); for(int i=1;i<=m;++i) SAM::get(); SAM::solve(); SAM::init(); }return 0; }
轉載于:https://www.cnblogs.com/guangheli/p/10679544.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的[十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java8.0安装教程_jdk8安装教程
- 下一篇: 前端需要了解的http知识