「十二省联考2019」 字符串问题

Description

给出一个字符串\(S\)和若干个\(A\)类串和\(B\)类串,保证两类串都是\(S\)的子串,并且在给出若干组支配关系\((x,y)\)表示一个编号为\(x\)\(A\)类串支配编号为\(y\)\(B\)类串,现在你需要求出一个长度最长的\(T\)串满足\(T\)可以被划分为若干个\(A\)类串,并且第\(i\)\(A\)类串存在一个支配的\(B\)类串是第\(i+1\)\(A\)类串的前缀

Solution

考虑我们如果将一个\(B\)类串向所有该串是其前缀的\(A\)类串连边,然后在将支配关系连边,那么我们得到的必定是一个\(DAG\),否则无解,所以只需要对其进行一遍拓扑排序\(DP\)就好了

考虑怎么进行连边,这里首先考虑对反串建出一个\(SAM\),那么一个\(B\)类串就只需要向他在\(parent\)树上的子树中所有\(A\)类串连边就好了,但是由于会存在某些\(A\)类串和\(B\)类串在同一个点上,所以我们需要将这些点按照串长将他从\(parent\)树中拆出来就好了,最后子树连边直接做一遍\(DFS\)序然后线段树优化建图即可,复杂度\(O(n\log n+m\log n)\)

事实上还有一种优秀的建图方式,就是考虑每个\(A\)类串直接由离他最近的一个祖先\(B\)类串连边就好了,这样就可以将点数和边数都做到\(O(n)\)

Code

//Created Time:2020年05月04日 星期一 20时50分42秒
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000006
#define pi pair<int, int>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back

using namespace std;

void chmax(long long &x, long long y){x = x >= y ? x : y;}

int n, na, nb, m, tot, num;
int pos[N], ida[N], idb[N], lena[N], lenb[N], sz[N], dfn[N], book[N], id[N], deg[N];
char s[N];
long long dis[N];

vector<pi> nd[N], e[N];
vector<int> son[N];

int read();
void work();
bool cmp(pi ,pi);
void build(int ,int ,int);
void link(int ,int ,int ,int ,int, int);

//{{{SAM
struct SAM{
	int cnt, lst;
	int fa[N], len[N], ch[N][26], d[N], rk[N], fath[N][20];

	void clear(){
		for(int i = 1; i <= cnt; ++i){
			id[i] = dfn[i] = sz[i] = book[i] = d[i] = fa[i] = len[i] = 0;
			memset(ch[i], 0, sizeof ch[i]);
			memset(fath[i], 0, sizeof fath[i]);
			vector<pi>().swap(nd[i]);
			vector<int>().swap(son[i]);
		}
		cnt = lst = 1; tot = 0;
		return ;
	}

	int ins(int x){
		int np = ++cnt, p = lst; lst = np; len[np] = len[p] + 1;
		for(; p && !ch[p][x]; p = fa[p]) ch[p][x] = np;
		if(!p) fa[np] = 1;
		else{
			int q = ch[p][x];
			if(len[q] == len[p] + 1) fa[np] = q;
			else{
				int nq = ++cnt; memcpy(ch[nq], ch[q], sizeof ch[nq]);
				fa[nq] = fa[q]; fa[q] = fa[np] = nq; len[nq] = len[p] + 1;
				for(; p && ch[p][x] == q; p = fa[p]) ch[p][x] = nq;
			}
		}
		return np;
	}

	void get(){
		for(int i = 1; i <= cnt; ++i) ++d[len[i]];
		for(int i = 1; i <= cnt; ++i) d[i] += d[i - 1];
		for(int i = cnt; i >= 1; --i) rk[d[len[i]]--] = i;
		for(int i = 1; i <= cnt; ++i){
			int u = rk[i]; fath[u][0] = fa[u];
			for(int j = 1; j <= 19; ++j)
				fath[u][j] = fath[fath[u][j - 1]][j - 1];
		}
		return ;
	}

	void find(int u, int x, int flag){
		for(int i = 19; ~i; --i)
			if(len[fath[u][i]] >= (flag ? lena[x] : lenb[x]))
				u = fath[u][i];
		nd[u].pb(mkp(x, flag));
		return ;
	}

	void dfs(int u){
		dfn[u] = tot;
		if(book[u] >= 2) dfn[u] = ++tot, sz[u] = 1, lena[tot] = len[u];
		for(auto v : son[u])
			dfs(v), sz[u] += sz[v];
		return ;
	}


	void rebuild(){
		for(int i = 1; i <= cnt; ++i){
			if(nd[i].empty()) continue;
			sort(nd[i].begin(), nd[i].end(), cmp);
			int lst = fa[i];
			for(auto p : nd[i]){
				int u = p.fi, op = p.se;
				if(len[lst] == (op ? lena[u] : lenb[u]) && book[lst] != op + 1) book[lst] = 3;
				else if(len[lst] != (op ? lena[u] : lenb[u])){
					fa[++cnt] = lst; len[cnt] = op ? lena[u] : lenb[u];
					book[cnt] = op + 1; lst = cnt;
				}
				(op ? ida[u] : idb[u]) = lst;
				continue;
			}
			fa[i] = lst;
		}
		for(int i = 1; i <= cnt; ++i) son[fa[i]].push_back(i);
		dfs(1); return ;
	}
}sam;
//}}}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
#endif
	int T; cin >> T;
	while(T--)
		work();
	return 0;
}

int read(){
	int x = 0; char ch = getchar();
	for(; !isdigit(ch); ch = getchar());
	for(; isdigit(ch); ch = getchar()) x = x * 10 + (ch - '0');
	return x;
}

bool cmp(pi x, pi y){
	int lenx = x.se ? lena[x.fi] : lenb[x.fi];
	int leny = y.se ? lena[y.fi] : lenb[y.fi];
	return lenx < leny;
}

void work(){
	scanf("%s", s + 1); n = strlen(s + 1);
	sam.clear(); long long ans = 0;
	for(int i = n; i; --i) pos[i] = sam.ins(s[i] - 'a');
	na = read(); sam.get();
	for(int i = 1; i <= na; ++i){
		int l = read(), r = read();
		lena[i] = r - l + 1;
		chmax(ans, lena[i] * 1ll);
		sam.find(pos[l], i, 1);
	}
	nb = read();
	for(int i = 1; i <= nb; ++i){
		int l = read(), r = read();
		lenb[i] = r - l + 1;
		sam.find(pos[l], i, 0);
	}
	num = 0; sam.rebuild();
	build(1, 1, tot);
	int lim = num;
	for(int i = 1; i <= nb; ++i){
		int u = idb[i]; idb[i] = ++num;
		if(book[u] != 3)
			link(1, 1, tot, dfn[u] + 1, dfn[u] + sz[u], num);
		else link(1, 1, tot, dfn[u], dfn[u] + sz[u] - 1, num);
	}
	m = read();
	for(int i = 1; i <= m; ++i){
		int x = read(), y = read();
		e[id[dfn[ida[x]]]].pb(mkp(idb[y], 0));
		++deg[idb[y]];
	}
	queue<int> Q;
	for(int i = 1; i <= num; ++i){
		dis[i] = 0;
		if(!deg[i]){
			if(i > lim) dis[i] = -999999999;
			Q.push(i);
		}
	}
	int cnt = 0;
	while(!Q.empty()){
		int u = Q.front(); Q.pop(); ++cnt;
		chmax(ans, dis[u]);
		for(auto p : e[u]){
			int v = p.fi, w = p.se;
			chmax(dis[v], dis[u] + w);
			--deg[v];
			if(!deg[v]) Q.push(v);
		}
	}
	if(cnt != num) puts("-1");
	else printf("%lld\n", ans);
	for(int i = 1; i <= num; ++i) 
		vector<pi>().swap(e[i]), deg[i] = 0;
	return ;
}

#define ls pos << 1
#define rs pos << 1 | 1
void build(int pos, int l, int r){
	num = max(num, pos);
	if(l == r) return id[l] = pos, void();
	int mid = (l + r) >> 1;
	build(ls, l, mid); build(rs, mid + 1, r);
	if(l == mid) e[pos].pb(mkp(ls, lena[l]));
	else e[pos].pb(mkp(ls, 0)); 
	if(mid + 1 == r) e[pos].pb(mkp(rs, lena[r]));
	else e[pos].pb(mkp(rs, 0));
	++deg[ls]; ++deg[rs];
	return ;
}

void link(int pos, int l, int r, int nl, int nr, int p){
	if(nl > nr) return ;
	if(l >= nl && r <= nr){
		++deg[pos];
		if(l == r) 
			e[p].pb(mkp(pos, lena[l]));
		else e[p].pb(mkp(pos, 0));
		return ;
	}
	int mid = (l + r) >> 1;
	if(mid >= nl) link(ls, l, mid, nl, nr, p);
	if(mid < nr) link(rs, mid + 1, r, nl, nr, p);
	return ;
}
#undef ls
#undef rs
posted @ 2020-06-10 16:25  Roal_L  阅读(150)  评论(0编辑  收藏  举报