【十二省联考2019】字符串问题【后缀自动机】【拓扑排序】
題意:給一個(gè)字符串 SSS,以子串的形式給出一些 A 類串和 B 類串以及 mmm 對(duì) A 類串支配 B 類串的關(guān)系。求一個(gè)總長(zhǎng)度最長(zhǎng)的 A 類串序列,使得每個(gè)串都存在一個(gè) B 類串前綴被后一個(gè)串支配。無(wú)窮輸出 ?1-1?1。
∣S∣,m≤2×105|S|,m\leq 2\times 10^5∣S∣,m≤2×105
顯然需要先把反串 SAM 建出來(lái),然后樹(shù)上倍增定位每個(gè)子串。
對(duì)于每一步,相當(dāng)于是可以不斷丟掉最后一個(gè)字符變成前綴,如果是個(gè) B 類串,可以走到一個(gè)被支配的 A 類串。
所以 SAM 上每個(gè)點(diǎn)向 fail 樹(shù)上的父親連一條 000 的邊, uuu 支配 vvv 就從 vvv 往 uuu 連 ∣u∣|u|∣u∣ 的邊。然后拓?fù)渑判蚝笞鲎铋L(zhǎng)路即可。因?yàn)闆](méi)有 000 環(huán),也沒(méi)有不聯(lián)通等奇奇怪怪的東西,所以有環(huán)就是 ?1-1?1。
然后這道題就做完了……
才怪。你的 A,B 類串定位的是一類子串,而限制是死的。具體來(lái)說(shuō),一個(gè) B 類串定位到了某個(gè)結(jié)點(diǎn)的一坨子串上,然后一個(gè) A 類串定位也定位到了這個(gè)結(jié)點(diǎn),而且還比 B 類串短,那么它是不能走到這個(gè) B 的。但是 SAM 上它們是同一個(gè)結(jié)點(diǎn),所以上面的算法就會(huì)出錯(cuò)。
怎么辦?拆了唄
當(dāng)然不用真的拆了,把這些定位的點(diǎn)放結(jié)點(diǎn)的 vector 里,然后在內(nèi)部排序建一下就可以了。
不算難寫,單純的碼量大。
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <vector> #include <utility> #include <queue> #include <algorithm> #define MAXN 800005 #define MAXM 1200005 using namespace std; typedef long long ll; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } char s[MAXN]; int ch[MAXN][26],len[MAXN],fa[MAXN],las=1,tot=1; void insert(int c) {int cur=++tot;len[cur]=len[las]+1;int p=las;for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;if (!p) fa[cur]=1;else{int q=ch[p][c];if (len[q]==len[p]+1) fa[cur]=q;else{int _q=++tot;len[_q]=len[p]+1;fa[_q]=fa[q],fa[cur]=fa[q]=_q;memcpy(ch[_q],ch[q],sizeof(ch[_q]));for (;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=_q;}}las=cur; } int up[MAXN][20]; inline void build() {for (int i=1;i<=tot;i++) up[i][0]=fa[i];for (int j=1;j<20;j++)for (int i=1;i<=tot;i++)up[i][j]=up[up[i][j-1]][j-1]; } inline int find(int x,int l) {for (int i=19;i>=0;i--)if (len[up[x][i]]>=l)x=up[x][i];return x; } int na,nb,la[MAXN],lb[MAXN],ra[MAXN],rb[MAXN]; struct node { int idx,type;inline int len()const{return type? ra[idx]-la[idx]+1:rb[idx]-lb[idx]+1;}inline int pos()const{if (type==1) return tot+idx;if (type==0) return tot+na+idx;return idx;} }; inline bool operator <(const node& a,const node& b){return a.len()<b.len()||(a.len()==b.len()&&a.type<b.type);} vector<node> lis[MAXN]; struct edge{int u,v,w;}e[MAXM]; int head[MAXN],nxt[MAXM],deg[MAXN],cnt; inline void addnode(int u,int v,int w) {e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt;++deg[v]; } int pos[MAXN],dfn[MAXN],tim; ll dp[MAXN]; int q[MAXN],qs,qt; void clear() {for (int i=1;i<=tot;i++) {memset(ch[i],0,sizeof(ch[i])),fa[i]=len[i]=0;memset(up[i],0,sizeof(up[i]));vector<node>().swap(lis[i]);}for (int i=1;i<=tot+na+nb+1;i++) head[i]=deg[i]=dp[i]=0;for (int i=1;i<=cnt;i++) nxt[i]=0;tim=cnt=0,las=tot=1; } int main() {for (int T=read();T;T--){scanf("%s",s+1);int n=strlen(s+1);for (int i=n;i>=1;i--) insert(s[i]-'a'),pos[i]=las;build(); na=read();for (int i=1;i<=na;i++){la[i]=read(),ra[i]=read();lis[find(pos[la[i]],ra[i]-la[i]+1)].push_back((node){i,1});}nb=read();for (int i=1;i<=nb;i++){lb[i]=read(),rb[i]=read();lis[find(pos[lb[i]],rb[i]-lb[i]+1)].push_back((node){i,0});}for (int i=1;i<=tot;i++) {sort(lis[i].begin(),lis[i].end());lis[i].push_back((node){i,-1});if (fa[i]) addnode(lis[i].front().pos(),fa[i],0);for (int j=0;j<(int)lis[i].size()-1;j++) addnode(lis[i][j+1].pos(),lis[i][j].pos(),0);}for (int m=read();m;m--){int u,v;u=read(),v=read();addnode(tot+na+v,tot+u,ra[u]-la[u]+1);}int S=tot+na+nb+1;for (int i=1;i<=na;i++) addnode(S,tot+i,ra[i]-la[i]+1);qs=1,qt=0;for (int i=1;i<=S;i++) if (!deg[i]) q[++qt]=i;while (qs<=qt){int u=dfn[++tim]=q[qs++];for (int i=head[u];i;i=nxt[i])if (!(--deg[e[i].v]))q[++qt]=e[i].v;}if (tim<S) puts("-1");else{for (int k=S;k>=1;k--){int u=dfn[k];for (int i=head[u];i;i=nxt[i])dp[u]=max(dp[u],dp[e[i].v]+e[i].w);}printf("%lld\n",dp[S]);}clear();}return 0; }總結(jié)
以上是生活随笔為你收集整理的【十二省联考2019】字符串问题【后缀自动机】【拓扑排序】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 儿童减肥的最好方法是什么
- 下一篇: 【NOI2018】你的名字【后缀自动机】