题目
内容
给出字符串s, ∣ s ∣ ≤ 1 e 5 |s|\le 1e^5 ∣s∣≤1e5,有n个询问,每个询问给出字符串 m i m_i mi,和整数 k i k_i ki(每个字符串都不相同),求s的一个子串,字符串 m i m_i mi出现了 k i k_i ki次,求子串的最短长度,如果不存在这样的子串输出-1.
∑ 1 n ∣ m i ∣ ≤ 1 e 5 \sum_1^n |m_i|\le1e^5 ∑1n∣mi∣≤1e5
分析
对于每个询问的串,如果我们可以找到这个串出现的所有位置,那么就可以很容易解决这个问题.
用n个询问的字符串
m
i
m_i
mi建AC自动机. 我们可以对字符串s每一个位置都暴力跳fail,找到有哪些询问串以这个位置结尾.
但是暴力跳fail,时间复杂度为O(
n
2
n^2
n2),如果s串是1
e
5
e^5
e5 个a组成的串,m串也1
e
5
e^5
e5个a .那么暴力跳fail将瞬间爆炸
因此我们还需要进一步优化:
我们定义match数组:match[i]为i节点的最长后缀,并且这个后缀是n个询问串中的一个.如果不存在这样的后缀那么match[i]为AC自动机的根节点.
由于限制了询问字符串的总长度,而长度不同的字符串不超过
∑
1
n
∣
m
i
∣
\sqrt{\sum_1^n|m_i|}
∑1n∣mi∣ 个,而每一次跳match数组,当前节点所表示的字符串长度至少减小1,因此最多会跳O(
n
\sqrt n
n) 次,
由于一共只跳
n
n
n\sqrt n
nn 次,因此所有询问串的出现位置 也不会超过
n
n
n\sqrt n
nn ,我们有足够的空间存下所有的出现位置.
最后我们可以在
O
(
n
n
)
O(n\sqrt n)
O(nn) 内找出所有询问串的出现位置存入pos数组,最后遍历pos数组得出答案.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define debug(x) cerr<<#x<<' '<<x<<'\n'
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int maxn=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
struct Trie {
int next[maxn][26],fail[maxn],id[maxn],root,cnt;
int match[maxn];
int newnode() {
rep(i,0,25) next[cnt][i]=-1;
id[cnt++]=0;
return cnt-1;
}
void init() {
cnt=0;
root=newnode();
}
void ins(char a[],int x) {
int len=strlen(a),now=root;
rep(i,0,len-1) {
if(next[now][a[i]-'a']==-1)
next[now][a[i]-'a']=newnode();
now=next[now][a[i]-'a'];
}
id[now]=x;
}
void build() {
queue<int> q;
fail[root]=root;
rep(i,0,25) {
if(next[root][i]==-1) {
next[root][i]=root;
}
else {
fail[next[root][i]]=root;
match[next[root][i]]=root;
q.push(next[root][i]);
}
}
while(!q.empty()) {
int now=q.front();
q.pop();
rep(i,0,25) {
if(next[now][i]==-1) next[now][i]=next[fail[now]][i];
else {
fail[next[now][i]]=next[fail[now]][i];
q.push(next[now][i]);
int tmp=fail[next[now][i]];
if(id[tmp]>0) match[next[now][i]]=tmp;
else match[next[now][i]]=match[tmp];
}
}
}
}
}AC;
vi pos[maxn];
char str[maxn],a[maxn];
int k[maxn],lena[maxn];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin>>str;
int n;
cin>>n;
AC.init();
rep(i,1,n) {
cin>>k[i]>>a;
lena[i]=strlen(a);
AC.ins(a,i);
}
AC.build();
int len=strlen(str),now=0;
rep(i,0,len-1) {
now=AC.next[now][str[i]-'a'];
for(int t=now;t;t=AC.match[t]) {
if(AC.id[t]) pos[AC.id[t]].pb(i);
}
}
rep(i,1,n) {
if(k[i]>pos[i].size()) cout<<-1<<'\n';
else {
int ans=inf;
for(int l=0,r=k[i]-1;r<pos[i].size();l++,r++) {
ans=min(ans,pos[i][r]-pos[i][l]+lena[i]);
}
cout<<ans<<'\n';
}
}
return 0;
}