「BZOJ3145」Str-SAM

Description

给定串 S , T S,T S,T。求 T T T S S S中出现的最长子串。

定义一个串 B B B匹配与 A A A当且仅当 ∣ A ∣ = ∣ B ∣ |A|=|B| A=B且至多存在一个 i i i满足 A i ≠ B i A_i \neq B_i Ai=Bi

Solution

考虑枚举题目中的 i i i,即模糊匹配位置。

设它在 T T T串中的位置为 i i i,则要找到在 S S S串中的位置 j j j使得 l c p ( T [ i + 1 , m ] , S [ j + 1 , n ] ) + l c s ( S [ 1 , i − 1 ] , T [ 1 , j − 1 ] ) lcp(T[i+1,m],S[j+1,n])+lcs(S[1,i-1],T[1,j-1]) lcp(T[i+1,m],S[j+1,n])+lcs(S[1,i1],T[1,j1])

分别建立前缀树和后缀树,那么题目等价于找两个点,使得它们在两棵树上的 l c a lca lca深度之和最大。

在第一棵树上 D F S DFS DFS,维护按第二棵树上的 d f s dfs dfs序排序的 s e t set set。每次合并时算一下答案。

#include <bits/stdc++.h>
using namespace std;

inline int gi()
{
	char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	int sum = 0;
	while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

inline void chkmax(int &a, int b) {if (a < b) a = b;}

const int maxn = 400005;

int n, m, ans;
char s[maxn], t[maxn];

struct SAM
{

	int rt = 1, tot = 1, lst = 1, fa[maxn], Id[maxn], pos[maxn], len[maxn], ch[maxn][27];
	
	void extend(int c, int i)
	{
		int pre = lst, x = ++tot;
		len[x] = len[pre] + 1; Id[x] = i; pos[i] = lst = x;
		while (pre && !ch[pre][c]) ch[pre][c] = x, pre = fa[pre];
		if (!pre) fa[x] = rt;
		else {
			int k = ch[pre][c];
			if (len[k] == len[pre] + 1) fa[x] = k;
			else {
				int y = ++tot;
				memcpy(ch[y], ch[k], sizeof(ch[y]));
				len[y] = len[pre] + 1; fa[y] = fa[k]; fa[k] = fa[x] = y;
				while (pre && ch[pre][c] == k) ch[pre][c] = y, pre = fa[pre];
			}
		}
	}
	
} sam1, sam2;

vector<int> to1[maxn], to2[maxn];
int Time, son[maxn], siz[maxn], top[maxn], dfn[maxn];
struct cmp {
	bool operator() (int a, int b) {
		return dfn[sam2.pos[sam1.Id[a] - 2]] <= dfn[sam2.pos[sam1.Id[b] - 2]];
	}
};
set<int, cmp> tmp[maxn << 1];
set<int, cmp> *S[maxn], *T[maxn];

void dfs1(int u)
{
	siz[u] = 1; dfn[u] = ++Time;
	for (int v : to2[u]) {
		dfs1(v);
		siz[u] += siz[v];
		if (siz[v] >= siz[son[u]]) son[u] = v;
	}
}

void dfs2(int u)
{
	if (son[u]) top[son[u]] = top[u], dfs2(son[u]);
	for (int v : to2[u])
		if (v != son[u]) top[v] = v, dfs2(v);
}

int lca(int u, int v)
{
	while (top[u] != top[v]) {
		if (sam2.len[top[u]] > sam2.len[top[v]]) u = sam2.fa[top[u]];
		else v = sam2.fa[top[v]];
	}
	return sam2.len[u] <= sam2.len[v] ? u : v;
}

int dist(int u, int v)
{
	if (min(sam1.Id[u], sam1.Id[v]) <= 1 || max(sam1.Id[u], sam1.Id[v]) <= n + 2) return 0;
	return sam2.len[lca(sam2.pos[sam1.Id[u] - 2], sam2.pos[sam1.Id[v] - 2])] + 1;
}

void calc(set<int, cmp> *S, set<int, cmp> *T, int len)
{
	bool flg = 0;
	if (S -> size() > T -> size()) swap(S, T), flg = 1;
	for (int u : (*S)) {
		auto it = T -> lower_bound(u);
		if (it != T -> begin()) --it, chkmax(ans, dist(u, *it) + len), ++it;
		if (it != T -> end()) chkmax(ans, dist(u, *it) + len);
	}
	if (flg) swap(S, T);
}

void merge(set<int, cmp> *&S, set<int, cmp> *&T)
{
	if (S -> size() < T -> size()) swap(S, T);
	for (int u : (*T)) S -> insert(u);
}

void dfs(int u)
{
	S[u] = &tmp[u * 2 - 1]; T[u] = &tmp[u * 2];
	if (1 <= sam1.Id[u] && sam1.Id[u] <= n) S[u] -> insert(u);
	else if (n + 1 < sam1.Id[u] && sam1.Id[u] <= n + m + 1) T[u] -> insert(u);
	for (int v : to1[u]) {
		dfs(v);
		calc(S[u], T[v], sam1.len[u]);
		calc(S[v], T[u], sam1.len[u]);
		merge(S[u], S[v]);
		merge(T[u], T[v]);
	}
}

int main()
{
	scanf("%s", s + 1); n = strlen(s + 1);
	scanf("%s", t + 1); m = strlen(t + 1);

	for (int i = m; i >= 1; --i) sam1.extend(t[i] - 'a', n + i + 1);
	sam1.extend(26, n + 1);
	for (int i = n; i >= 1; --i) sam1.extend(s[i] - 'a', i);
	
	for (int i = 1; i <= n; ++i) sam2.extend(s[i] - 'a', i);
	sam2.extend(26, n + 1);
	for (int i = 1; i <= m; ++i) sam2.extend(t[i] - 'a', n + i + 1);
	
	for (int i = 2; i <= sam1.tot; ++i) to1[sam1.fa[i]].push_back(i);
	for (int i = 2; i <= sam2.tot; ++i) to2[sam2.fa[i]].push_back(i);
	
	dfs1(1);
	dfs2(1);
	dfs(1);

	printf("%d\n", ans);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值