首页 > 其他分享 >「ARC151E」Keep Being Substring - 题解

「ARC151E」Keep Being Substring - 题解

时间:2022-10-24 20:46:31浏览次数:91  
标签:子串 字符 Being 题解 Substring int 公共 sa include

题意

题目链接:Link

有一个序列 \(A\)。\(X,Y\) 是给定的 \(A\) 的两个子串,每次可以在 \(X\) 的开头或末尾增添或删除一个数字,且需满足任意时刻 \(X\) 非空且为 \(A\) 的子串,求把 \(X\) 变成 \(Y\) 的最少次数。

题解

先考虑如果 \(X,Y\) 有公共字串的情况。不妨设 \(X,Y\) 的最长公共子串为 \(S\)。

于是显然有一种方法,先将 \(X\) 一直删,直到剩下 \(S\),然后再添加补成 \(Y\),这样操作数是 \(|X| + |Y| - 2 |S|\)。

求最长公共子串可以用 SA,复杂度 \(\mathcal{O}(n \log n)\)。

当然也可以不用 SA。考虑哈希,然后二分一个长度,枚举 \(X\) 该长度的子串,然后用 map 存下 \(Y\),匹配即可。复杂度 \(\mathcal{O}(n \log^2 n)\)。

然后考虑没有公共字串的情况。这意味着 \(X,Y\) 没有相同字符。

有一个贪心。

如果 \(X\) 和 \(Y\) 没有公共字串,并且 \(|X| \ge 2\) 时,优先执行删除操作一定不劣。

根据这个贪心,我们可以一直删 \(X\),直到 \(|X|=1\),然后再加一个字符。如果依然没有公共串,那么就再删一个字符。

于是当 \(X,Y\) 有公共字串的时候,一定是长度为 \(1\) 的串。那么有公共串后就变成上面那个问题,答案为 \(|X|+|Y|-2\)。

然后问题就是最后选哪个字符为公共串。考虑建图,对于 \(A\),将相邻的两个点连一条边权为 \(1\) 的边。于是问题就是以 \(A\) 中所有出现在 \(X\) 的字符为起点,以 \(A\) 中所有出现在 \(Y\) 中的字符为终点,求最短路。直接 bfs 即可。

最短路跑出来的最近的公共字符点中的路径,是要先加进来再删掉的,所以操作数为路径长度的 \(2\) 倍。

那么为什么这个贪心是正确的?

因为不存在公共串,所以最开始出现在 \(|X|\) 的串中的字符一定是要被删掉的。然后我们会找到一个最近的公共字符的位置 \(p\),即使我们先不删除 \(p\) 到原先串之间的路径,之后也需要删除到只剩下公共串,也就是位置 \(p\)。

但是否会出现串到位置 \(p\) 之间的字符出现,先被删,再加进来搭路到 \(p\),再删掉的情况?可以发现,如果存在这种情况,那么这个操作数一定不是最短路径。

复杂度 \(\mathcal{O}(n \log n)\)。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int N=4e5+5;
struct edge{
	int v,nx;
}e[N<<1];
int n,nb,nc,cnt,a[N],b[N],c[N],s[N],sa[N],height[N],rk[N];
int len,ans,ne,f[N],deep[N];
bool vb[N],vc[N];
queue<int> q;
void SA()
{	static int m=n+1,x[N],y[N],cc[N];
	for(int i=0;i<=max(m,cnt);i++)x[i]=y[i]=cc[i]=0;
	for(int i=1;i<=cnt;i++)x[i]=s[i],cc[x[i]]++;
	for(int i=1;i<=m;i++)cc[i]+=cc[i-1];
	for(int i=cnt;i>=1;i--)sa[cc[x[i]]--]=i;
	for(int k=1;k<=cnt;k<<=1)
	{	int num=0;
		for(int i=cnt-k+1;i<=cnt;i++)y[++num]=i;
		for(int i=1;i<=cnt;i++)
			if(sa[i]>k)y[++num]=sa[i]-k;
		for(int i=1;i<=m;i++)cc[i]=0;
		for(int i=1;i<=cnt;i++)cc[x[i]]++;
		for(int i=2;i<=m;i++)cc[i]+=cc[i-1];
		for(int i=cnt;i>=1;i--)sa[cc[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y),num=1,x[sa[1]]=1;
		for(int i=2;i<=cnt;i++)
		{	if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])x[sa[i]]=num;
			else x[sa[i]]=++num;
		}
		if(num==cnt)break;
		m=num;
	}
}
void LCP()
{	int k=0;
	for(int i=1;i<=cnt;i++)rk[sa[i]]=i;
	for(int i=1;i<=cnt;i++)
	{	if(rk[i]==1)continue;
		if(k)k--;
		int j=sa[rk[i]-1];
		while(i+k<=cnt&&j+k<=cnt&&s[i+k]==s[j+k])k++;
		height[rk[i]]=k;
	}
}
int solve()
{	int res=0;
	for(int i=2;i<=cnt;i++)
		if((sa[i-1]<=nb&&sa[i]>nb)||(sa[i-1]>nb&&sa[i]<=nb))res=max(res,height[i]);
	return res;
}
void read(int u,int v)
{	e[++ne].v=v;
	e[ne].nx=f[u];
	f[u]=ne;
}
int main()
{	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	scanf("%d",&nb);
	for(int i=1;i<=nb;i++)
	{	scanf("%d",&b[i]);
		s[++cnt]=b[i],vb[b[i]]=1;
	}
	s[++cnt]=n+1; 
	scanf("%d",&nc);
	for(int i=1;i<=nc;i++)
	{	scanf("%d",&c[i]);
		s[++cnt]=c[i],vc[c[i]]=1;
	}
	SA(),LCP(),len=solve(),ans=n;
	if(len>0){printf("%d\n",nb+nc-(len<<1));return 0;}
	for(int i=1;i<n;i++)read(a[i],a[i+1]),read(a[i+1],a[i]);
	for(int i=1;i<=n;i++)
		if(vb[i])q.push(i),deep[i]=1;
	while(!q.empty())
	{	int u=q.front();q.pop();
		if(vc[u]){ans=deep[u]-1;break;}
		for(int i=f[u];i;i=e[i].nx)
		{	int v=e[i].v;
			if(!deep[v])q.push(v),deep[v]=deep[u]+1;
		}
	}
	printf("%d\n",(ans<<1)+nb+nc-2);
	return 0;
}

标签:子串,字符,Being,题解,Substring,int,公共,sa,include
From: https://www.cnblogs.com/Rainy7/p/solution-at-arc151e.html

相关文章

  • [abc274F] Fishing 题解
    比较有趣的用点思维的题。在学校和DYS一起推出来的题,庆祝AT复活写个题解。感觉用无序列表列出自己思绪的过程很简洁扼要,但是行文节奏过快。介于我想重现自己今天上午......
  • 「ARC140D」One to One - 题解
    题解若对每一块进行考虑,那么对于一个有\(n\)个点\(n\)条边的块,也就是基环树或环来说,里面一定不会存在\(a_i=-1\)。否则就是一棵树了,那么也最多只会有一个\(a_i=-1\)......
  • [NOI Online #1 入门组] 跑步 题解
    [NOIOnline#1入门组]跑步题解一个经典问题:计数将正整数\(n\)拆分为若干个正整数的方案数,这里拆成的正整数是无序的,对\(P\)取模。容易得到\(O(n^2)\)解法设\(f_{i,j......
  • Codeforces Round #401 (Div. 2) 题解 (待续)
    AShellGame#include<bits/stdc++.h>usingnamespacestd;#define#define#define#define#define#define#define#define#define#define#define#define#define#define#define......
  • April Fools Contest 2017 题解
    ANumbersJokeJoke数列,OEIS#include<bits/stdc++.h>usingnamespacestd;#define#define#define#define#define#define#define#define#define#define#define#define#define......
  • Codeforces Round #402 (Div. 1) 题解(待续)
    AStringGame#include<bits/stdc++.h>usingnamespacestd;#define#define#define#define#define#define#define#define#define#define#define#define#define#define#defin......
  • BZOJ 4747-4749题解 Usaco2016 Dec
    BZOJ4747:[Usaco2016Dec]CountingHaybales给出N(1≤N≤100,000)个数,和Q(1≤Q≤100,000)个询问。每个询问包含两个整数A,B(0≤A≤B≤1,000,000,000)。对于每个询问,给出数值......
  • Codeforces Round #395 (Div. 1) 题解
    ATimofeyandatree#include<bits/stdc++.h>usingnamespacestd;#define#define#define#define#define#define#define#define#define#define#define#define#define#defin......
  • Spring常见问题解决办法汇总
     解决Theprefix'context'forelement'context:component-scan'isnotbound<beansxmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://w......
  • BSOJ7075题解
    感觉这一类DP至少不应该被叫做“LCS模型”,本质应该是其他的东西......先来考虑经典的LCS:\(dp[n][m]\)表示\(S[n]\)和\(T[m]\)匹配上的最长的长度。那么我们不妨......