https://ac.nowcoder.com/acm/contest/5026/E
思路:其实就是找一个长度为
x
x
x的字串,使得它在字符串
s
s
s中至少出现了
k
k
k次,且这
k
k
k个
s
s
s均不相交。很显然,长度
x
x
x满足单调性,所以我们可以二分长度。那么如何快速判断两个字串是否相等呢?哈希。我们设进制为
b
a
s
e
base
base,
h
[
i
]
h[i]
h[i]表示
s
[
1
…
i
]
s[1…i]
s[1…i]的哈希值,那么有:
h
[
i
]
=
(
s
[
i
]
−
‘
a
’
)
+
(
s
[
i
−
1
]
−
‘
a
’
)
∗
b
a
s
e
+
…
+
(
s
[
i
−
l
e
n
+
1
]
)
∗
b
a
s
e
l
e
n
−
1
+
(
s
[
i
−
l
e
n
]
)
∗
b
a
s
e
l
e
n
)
+
…
h[i]=(s[i]-‘a’)+(s[i-1]-‘a’)*base+…+(s[i-len+1])*base^{len-1}+(s[i-len])*base^{len})+…
h[i]=(s[i]−‘a’)+(s[i−1]−‘a’)∗base+…+(s[i−len+1])∗baselen−1+(s[i−len])∗baselen)+…。我们再设
b
s
[
0
]
=
1
,
b
s
[
i
]
=
b
s
[
i
−
1
]
∗
b
a
s
e
bs[0]=1,bs[i]=bs[i-1]*base
bs[0]=1,bs[i]=bs[i−1]∗base。那么子串
s
[
i
−
l
e
n
+
1
,
i
]
s[i-len+1,i]
s[i−len+1,i]的哈希值就等于
h
[
i
]
−
h
[
i
−
l
e
n
]
∗
b
a
s
e
l
e
n
h[i]-h[i-len]*base^{len}
h[i]−h[i−len]∗baselen。也就是说只要预处理出这两个数组,我们就可以
O
(
1
)
O(1)
O(1)得到任意子串的哈希值。现在考虑怎么
d
p
dp
dp。假设当前二分出的长度为
l
e
n
len
len,那么我们从
i
=
l
e
n
i=len
i=len开始向后扫,那么每次可以得到子串
s
[
i
−
l
e
n
+
1
,
i
]
s[i-len+1,i]
s[i−len+1,i]的哈希值
r
e
s
res
res,开两个哈希表
p
o
s
、
c
n
t
pos、cnt
pos、cnt,
p
o
s
[
i
]
pos[i]
pos[i]表示哈希值为
i
i
i的子串上一次出现的位置,
c
n
t
[
i
]
cnt[i]
cnt[i]表示哈希值为
i
i
i的子串的个数。那么如果
p
o
s
[
r
e
s
]
=
0
pos[res]=0
pos[res]=0,即该子串第一次出现,直接令
p
o
s
[
r
e
s
]
=
i
,
c
n
t
[
r
e
s
]
=
1
pos[res]=i,cnt[res]=1
pos[res]=i,cnt[res]=1即可;否则我们判断当前位置和上一次出现的位置之间的距离是否
>
=
l
e
n
>=len
>=len(因为不能相交),再做修改即可。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=2e5+5;
const int base=127;
int n,k;
char s[maxn];
ull h[maxn],bs[maxn];
unordered_map<ull,int> pos,cnt;
bool check(int len)
{
pos.clear(),cnt.clear();
ull tmp;
for(int i=len;i<=n;i++)
{
tmp=h[i]-h[i-len]*bs[len];
if(!pos[tmp])
pos[tmp]=i,cnt[tmp]=1;
else if(i-pos[tmp]>=len)
pos[tmp]=i,++cnt[tmp];
if(cnt[tmp]>=k)
return 1;
}
return 0;
}
int main()
{
scanf("%d%d%s",&n,&k,s+1);
bs[0]=1;
for(int i=1;i<=n;i++)
{
h[i]=h[i-1]*base+s[i]-'a';
bs[i]=bs[i-1]*base;
}
int l=1,r=n/k,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
l=mid+1;
else
r=mid-1;
}
printf("%d\n",r);
return 0;
}