链接
前置知识
首先要知道一个事情:所有查询串出现位置的总和不会超过 O ( n n ) O(n \sqrt n) O(nn)
因为长度相同的答案之和不会超过 O ( n ) O(n) O(n),而最多有 O ( n ) O(\sqrt n) O(n)种不同的长度
所以如果有一种做法是把所有的位置都给找出来,那么这种方法是可行的
做法1
ac自动机的暴力做法就是把查询串建出来自动机然后跑
但是这样有很多无用跳转,我直接把每个点能跳到的第一个有用的点记下来,那么这样每次操作都对答案有贡献
而答案的总规模已经说过了是 O ( n n ) O(n \sqrt n) O(nn),所以这种做法复杂度也是 O ( n n ) O(n \sqrt n) O(nn)
代码(做法2)
显然 s a m sam sam具有的天然的性质就是每个点的 e n d p o s endpos endpos都显示了这组串在哪些位置出现了
我只要能把这个信息给提取出来,而且在复杂度上不要有浪费,那么也可以保证算法的复杂度和答案同规模
显然我们需要一个排好序的 e n d p o s endpos endpos序列
一个很老的套路是 p a r e n t parent parent树上跑线段树合并,但是那样肯定会带一个 l o g log log, O ( n n log n ) O(n \sqrt n \log n) O(nnlogn)看起来很不好
其实,要求的就是在自动机上到的那个点 p p p的 e n d p o s endpos endpos集合,而且我们需要把这个集合有序化才能做后续操作,这个集合不能对每个点直接求(空间规模会达到 O ( n 2 ) O(n^2) O(n2)),而线段树合并在查询的时候又会带上一个 l o g log log
平衡树看起来不错(启发式合并即可),但是太难写
有一个解决方案是一边 d f s dfs dfs一边归并,我没写,但是感觉可能是对的
有一个性质:你所要求的就是一个子树上所有带有前缀的点的信息形成的有序序列
那么说到子树就是 d f s dfs dfs序了,所以现在只需要把一个区间里的有效信息提取出来并有序化即可
通过离散化,或者维护 n e x t a c t i v e next_active nextactive,都可以把信息提取出来,但是问题又来了,我不能排序,怎么有序化?
我这里用了一个类似桶排序的东西, a c c [ i ] acc[i] acc[i]是一个 v e c t o r vector vector,按顺序存储了“哪些询问”的答案包含这个点
最后再扫一遍就行了
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 100010
#define maxe 200010
#define cl(x) memset(x,0,sizeof(x))
#define rep(i,a,b) for(i=a;i<=b;i++)
#define drep(i,a,b) for(i=a;i>=b;i--)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
ll c, f(1);
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-0x30;
return f*x;
}
struct SAM
{
int tot, las, ch[maxn<<1][26], fa[maxn<<1], len[maxn<<1], pref[maxn<<1];
int* operator[](int u){return ch[u];}
void init()
{
int i;
rep(i,1,tot)cl(ch[i]),fa[i]=len[i]=pref[i]=0;
tot=las=1;
}
void append(int c, int tag=1)
{
int p(las);
len[las=++tot]=len[p]+1;
pref[las]=tag;
for(;p and !ch[p][c];p=fa[p])ch[p][c]=las;
if(!p)fa[las]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[las]=q;
else
{
int qq=++tot;
memcpy(ch[qq],ch[q],sizeof(ch[q]));
fa[qq]=fa[q];
len[qq]=len[p]+1;
fa[q]=fa[las]=qq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=qq;
}
}
}
}sam;
struct Graph
{
int etot, head[maxn<<1], to[maxe], next[maxe], w[maxe];
void clear(int N)
{
for(int i=1;i<=N;i++)head[i]=0;
etot=0;
}
void adde(int a, int b, int c=0){to[++etot]=b;w[etot]=c;next[etot]=head[a];head[a]=etot;}
#define forp(_,__) for(auto p=__.head[_];p;p=__.next[p])
}G;
struct Easy_Tree
{
int depth[maxn<<1], dist[maxn<<1], tid[maxn<<1], rtid[maxn<<1], tim, size[maxn<<1], rev[maxn<<1];
void dfs(int pos, int pre, Graph& G)
{
tid[pos]=++tim;
rev[tid[pos]]=pos;
size[pos]=1;
forp(pos,G)if(G.to[p]!=pre)
{
depth[G.to[p]]=depth[pos]+1;
dist[G.to[p]]=dist[pos]+G.w[p];
dfs(G.to[p],pos,G);
size[pos]+=size[G.to[p]];
}
rtid[pos]=tim;
}
void run(Graph& G, int root)
{
tim=0;
depth[root]=1;
dfs(1,0,G);
}
}et;
char s[maxn], t[maxn];
int m, k[maxn], n, next_active[maxn<<1], len[maxn];
vector<int> acc[maxn], v[maxn];
int calc(vector<int>& v, int k, int len)
{
int i, j, ans = iinf;
if(v.size()<k)return -1;
j=0;
rep(i,0,v.size()-1)
{
while(j<v.size() and j-i+1<k)j++;
if(j<v.size())
ans = min(ans,v[j]-v[i]+len);
}
return ans;
}
int main()
{
int i, kase;
scanf("%s%d",s+1,&m);
n = strlen(s+1);
sam.init();
rep(i,1,n)sam.append(s[i]-'a', i);
rep(i,2,sam.tot)G.adde(sam.fa[i],i);
et.run(G,1);
next_active[sam.tot+1]=iinf;
drep(i,sam.tot,0)
if(sam.pref[et.rev[i+1]])next_active[i]=i+1;
else next_active[i]=next_active[i+1];
rep(kase,1,m)
{
scanf("%d%s",k+kase,t+1);
len[kase] = strlen(t+1);
int p = 1;
rep(i,1,len[kase])p=sam[p][t[i]-'a'];
if(p==0)continue;
for(auto pp = next_active[ et.tid[p] - 1 ]; pp<=et.rtid[p]; pp=next_active[pp])
acc[sam.pref[et.rev[pp]]].emb(kase);
}
rep(i,1,n)for(auto x:acc[i])v[x].emb(i);
rep(kase,1,m)printf("%d\n",calc(v[kase],k[kase],len[kase]));
return 0;
}