题目大意:
给你一个字符串
s
s
s,有
n
n
n个询问,每次询问包含
k
i
k_i
ki和
m
i
m_i
mi。每个询问找到一个长度最小的字符串
t
t
t,满足
t
t
t是
s
s
s的子串,且
m
i
m_i
mi在
t
t
t中出现至少
k
i
k_i
ki次。
保证
m
i
m_i
mi不同。
∣
s
∣
<
=
1
0
5
|s|<=10^5
∣s∣<=105
n
<
=
1
0
5
n<=10^5
n<=105
∑
m
i
<
=
1
0
5
\sum m_i<=10^5
∑mi<=105
分析:
我们对询问离线,把询问插入一个AC自动机,然后把
s
s
s拿进去跑,就可以得出每一个询问串的匹配点(和sam的
r
i
g
h
t
right
right集类似)。现在相当于求中间有
k
k
k个匹配点的最小区间。我们其实可以直接暴力跑。
具体来说,我们对AC自动机维护一个
n
e
x
t
next
next,表示
f
a
i
l
fail
fail链上的下一个询问串(直接跳
f
a
i
l
fail
fail会T,因为有很多是某些询问串的前缀,这些没有意义),这些串就好多一个匹配点,然后更新这些串的
a
n
s
ans
ans,就可以求出解了。
为什么这能过呢?
我们考虑以每一个节点结尾最多有多少个询问串,显然这些询问串一定长度不同,因为保证询问串不同。那么最多有
n
\sqrt{n}
n个,所以总复杂度是
O
(
n
n
)
O(n\sqrt{n})
O(nn)的。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <queue>
const int maxn=1e5+7;
const int inf=0x3f3f3f3f;
using namespace std;
char s[maxn],s1[maxn];
int m,k,cnt;
int ans[maxn];
queue <int> q;
struct node{
int fail,next,ans,k,num,len;
int son[26];
vector <int> a;
}t[maxn];
void ins(char *S,int d,int k)
{
int len=strlen(S+1),now=0;
for (int i=1;i<=len;i++)
{
int c=S[i]-'a';
if (!t[now].son[c]) t[now].son[c]=++cnt;
t[t[now].son[c]].len=t[now].len+1;
now=t[now].son[c];
}
t[now].k=k,t[now].num=d,t[now].ans=inf;
}
void getfail()
{
for (int i=0;i<26;i++)
{
if (t[0].son[i])
{
t[i].fail=0;
q.push(t[0].son[i]);
}
}
while (!q.empty())
{
int now=q.front();
q.pop();
if (t[t[now].fail].num) t[now].next=t[now].fail;
else t[now].next=t[t[now].fail].next;
for (int i=0;i<26;i++)
{
if (t[now].son[i])
{
t[t[now].son[i]].fail=t[t[now].fail].son[i];
q.push(t[now].son[i]);
}
else
{
t[now].son[i]=t[t[now].fail].son[i];
}
}
}
}
void find(char *S)
{
int len=strlen(S+1),now=0;
for (int i=1;i<=len;i++)
{
int c=S[i]-'a';
now=t[now].son[c];
for (int p=now;p;p=t[p].next)
{
if (t[p].num)
{
t[p].a.push_back(i);
if (t[p].a.size()>=t[p].k)
{
int sz=t[p].a.size();
t[p].ans=min(t[p].ans,i-(t[p].a[sz-t[p].k]-t[p].len));
}
}
}
}
}
int main()
{
scanf("%s",s+1);
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%d",&k);
scanf("%s",s1+1);
ins(s1,i,k);
}
getfail();
find(s);
for (int p=1;p<=cnt;p++)
{
if (t[p].num)
{
if (t[p].ans!=inf) ans[t[p].num]=t[p].ans;
else ans[t[p].num]=-1;
}
}
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
}