列表

详情


NC202499. 相似的子串

描述

   给定一个字符串,要求取出k个位置不相交的子串,且他们之间任意两个的最长公共前缀的长度均不小于x。现在给出k,求最大的x
   两个字符串str1,str2的公共前缀为x指str1和str2的长度均不小于x且这两个字符串的前x个字符对应相同。最长公共前缀即所有的公共前缀里最长的那个,如没有公共前缀则视为最长公共前缀长度为0。

输入描述

第一行两个正整数n,k(2≤n≤200000,2≤k≤n)。第二行一个长度为n的仅包含小写英文字母的字符串。

输出描述

仅一行一个整数x代表答案。

示例1

输入:

7 3 
abcabab

输出:

2

说明:

一种可行的方案:取出的三个子串分别为abc,ab,ab时,他们之间的位置并不相交且任意两个的最长公共前缀均为ab。

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 1347ms, 内存消耗: 16196K, 提交时间: 2020-04-11 14:10:29

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,mod=1e9+7;
typedef unsigned long long ull;
ull h[N],p[N]={1};
char s[N]; 
int n,k,ans;
bool fun(int len){
	 map<ull,pair<int,int> >mp;
	 for(int i=len;i<=n;i++){
	 	ull v=h[i]-h[i-len]*p[len];
	 	if(i-mp[v].first>=len) mp[v].first=i,mp[v].second++;
	 	if(mp[v].second>=k) return 1;
	 }
	 return 0;
}
int main(){
	scanf("%d%d",&n,&k);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)//预处理 
		p[i]=p[i-1]*233,h[i]=h[i-1]*233+s[i];
	int l=1,r=n/k;
	while(l<=r){
		int mid=(l+r)>>1;
		if(fun(mid)) ans=mid,l=mid+1;
		else r=mid-1; 
	}
	printf("%d\n",ans);
	return 0;
}

C++(clang++ 11.0.1) 解法, 执行用时: 710ms, 内存消耗: 16276K, 提交时间: 2022-11-02 09:54:15

#include<bits/stdc++.h>
using namespace std;
char s[1000001];
long long n,m,k,ans,ha[1000001],p[1000001];
inline bool check(long long x){
	map<long long,pair<long long,long long> >mp;
	for(int i=x;i<=n;++i){
		long long tmp=ha[i]-ha[i-x]*p[x];
		if(i-mp[tmp].first>=x)mp[tmp].first=i,mp[tmp].second++;
		if(mp[tmp].second>=k)return true;
	}
	return false;
}
int main(){
	cin>>n>>k>>s+1;p[0]=1;
	for(int i=1;i<=n;++i)p[i]=p[i-1]*131,ha[i]=ha[i-1]*131+s[i];
	int l=1,r=n/k;
	while(l<=r){
		long long mid=(l+r)/2;
		if(check(mid))l=mid+1,ans=mid;
		else r=mid-1;
	}
	cout<<ans;
}

上一题