[Luogu] P5546 公共串

题意:求n个字符串的最长公共子串

思路:后缀数组+二分
1.若是求两个字符串的最长公共子串的长度且两字符串不太长本可以用dp
2. 因为后缀数组方便处理子串问题,所以可以将问题转化成特殊的子串问题。所以将若干字符串拼成一个字符串s,并记录这个长串的每一部分属于哪个短的字符串。
3. 但是这跟求子串的最长重叠长度不同,子串是“分组”的。
4. 所求的不是具体的串而是长度,且这个公共串有单调性,即公共串越短越能满足条件,越长越不能满足条件。所以考虑二分答案。
5. 如何check?若存在最长的公共子串,那么他们一定连续地存在于s按字典序排成的后缀中。所以只要检验是否存在一段连续的height值大于now,且他们存在于n个不同的组中即可。
6. 连接的时候用的字符一定不能是一样的,否则他们连续地存在于按字典序排列的后缀中,会造成答案错误。我就这样wa了一次

#include<bits/stdc++.h>
#define FAST ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn = 1e5+5;

using namespace std;

int t;
int n,m;
char s[maxn],str[maxn];
int pos[maxn],vis[10];
int sa[maxn],rk1[maxn],tp1[maxn],tax[maxn],height[maxn];
int *rk=rk1, *tp=tp1;

void bucket()
{
	for (int i=0; i<=m; i++) tax[i]=0;
	for (int i=1; i<=n; i++) tax[rk[i]]++;
	for (int i=1; i<=m; i++) tax[i]+=tax[i-1];
	for (int i=n; i>=1; i--) sa[tax[rk[tp[i]]]--]=tp[i];	
}

void get_sa()
{
	m=128;
	for (int i=1; i<=n; i++) rk[i]=s[i],tp[i]=i;
	bucket();
	for (int k=1,p=0; p<n; m=p,k<<=1)
	{
		p=0;
		for (int i=1; i<=k; i++) tp[++p]=n-k+i;
		for (int i=1; i<=n; i++)
			if (sa[i]>k) tp[++p]=sa[i]-k;
		bucket();
		swap(tp,rk);
		rk[sa[1]]=p=1;
		for (int i=2; i<=n; i++)
			rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+k]==tp[sa[i]+k])?p:++p; 
	}
}

void get_height()
{
	int k=0;
	for (int i=1; i<=n; i++)
	{
		if (k) k--;
		int j=sa[rk[i]-1];
		while(s[i+k]==s[j+k]) k++;
		height[rk[i]]=k;
	}
}

bool check(int now)
{
	for (int i=1; i<=n; i++)
	{
		if (height[i]<now) memset(vis,0,sizeof(vis));
		//[1...i-1]那些后缀不会是满足条件的一段
		vis[pos[sa[i]]]++;
		//但是这个后缀有可能成为新的一段的开始
		int flag=1;  //下面几行是检验有没有找到一段符合要求
		for (int j=1; j<=t; j++)
			if (!vis[j]) { flag=0; break; }
		if (flag==1) return true;
	}
	return false;	
}

int main()
{
   	FAST; 
   	cin>>t;

   	for (int i=1; i<=t; i++)
   	{
		cin>>str+1;
		for (int j=1; str[j]; j++) s[++n]=str[j],pos[n]=i;	
		s[++n]='z'+i;  //连接处不能一样
	}

	get_sa();
	get_height();
	
	int l=0,r=n+1;
	int ans=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if  (check(mid)) l=mid+1, ans=mid;
		else r=mid-1;
	}
	cout<<ans<<endl;
return 0;
}



我觉得check比较难写,这个check的算法可以拓展为一种检验是否存在一段连续区间满足某种性质的算法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值