題目鏈接:BZOJ3439
(tips:題目條件沒給全,底下所有字符串的長度之和大約不超過)
解法:字典樹+主席樹
先設法找到一個求kpm串的方法。按讀入的串反序構造字典樹,稱一個字符串的尾節點爲其在樹中的路徑中深度最大的那個點,記爲。那麼一個字符串的所有kmp串的尾節點全部位於以爲根的子樹內。
題目轉化爲求任意子樹內權值第大(注意有些節點不是任何字符串的尾節點,所以它的權值是空的,設成)。
然後給所有節點編上dfs序,主席樹維護即可。
時間複雜度(爲字符串總長)
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=13000001,M=100001,S=500001;
int root=1,tot,tot0=1,idx,dfn[M],lst[M],sum[N],lson[N],rson[N],son[S][26],pos[M],n,rt[S],k,nod[S];
char str[S];
vector<int> id[S];
inline int create(int x){sum[++tot]=sum[x],lson[tot]=lson[x],rson[tot]=rson[x];return tot;}
void insert(char *str,int j){
int u=root,m=strlen(str+1);
for(int i=m;i;--i){int &v=son[u][str[i]-'a'];if(!v)v=++tot0;u=v;}
id[u].push_back(j),pos[j]=u;
}
void dfs(int u){
nod[dfn[u]=++idx]=u;
for(int i=0;i<26;++i)if(son[u][i])dfs(son[u][i]);
lst[u]=idx;
}
void update(int &o,int p,int l,int r,int k,int x){
o=create(p);
if(l==r){sum[o]=x;return;}
int m=l+r>>1;if(m>=k)update(lson[o],lson[p],l,m,k,x);else update(rson[o],rson[p],m+1,r,k,x);
sum[o]=sum[lson[o]]+sum[rson[o]];
}
void update2(int &o,int l,int r,int k){
if(!o)o=++tot;
if(l==r){sum[o]=1;return;}
int m=l+r>>1;if(m>=k)update2(lson[o],l,m,k);else update2(rson[o],m+1,r,k);
sum[o]=sum[lson[o]]+sum[rson[o]];
}
int query(int o,int p,int l,int r,int k){
if(l==r)return l;
int m=l+r>>1,d=sum[lson[p]]-sum[lson[o]];
if(d>=k)return query(lson[o],lson[p],l,m,k);else return query(rson[o],rson[p],m+1,r,k-d);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%s",str+1),insert(str,i);
dfs(root);
for(int i=1;i<=tot0;++i)if(!id[nod[i]].size())rt[i]=rt[i-1];else{
update(rt[i],rt[i-1],1,n,id[nod[i]][0],1);
for(int j=1;j<id[nod[i]].size();++j)update(rt[i],rt[i],1,n,id[nod[i]][j],1);
}
for(int i=1;i<=n;++i){
scanf("%d",&k);
if(sum[rt[lst[pos[i]]]]-sum[rt[dfn[pos[i]]-1]]<k){puts("-1");continue;}
int x=query(rt[dfn[pos[i]]-1],rt[lst[pos[i]]],1,n,k);
printf("%d\n",x);
}
}