版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82757120
题目大意:
给你一个字符串
,有
个询问,每次询问包含
和
。每个询问找到一个长度最小的字符串
,满足
是
的子串,且
在
中出现至少
次。
保证
不同。
分析:
我们对询问离线,把询问插入一个AC自动机,然后把
拿进去跑,就可以得出每一个询问串的匹配点(和sam的
集类似)。现在相当于求中间有
个匹配点的最小区间。我们其实可以直接暴力跑。
具体来说,我们对AC自动机维护一个
,表示
链上的下一个询问串(直接跳
会T,因为有很多是某些询问串的前缀,这些没有意义),这些串就好多一个匹配点,然后更新这些串的
,就可以求出解了。
为什么这能过呢?
我们考虑以每一个节点结尾最多有多少个询问串,显然这些询问串一定长度不同,因为保证询问串不同。那么最多有
个,所以总复杂度是
的。
代码:
#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]);
}