题目链接
题目解法
我们可以把求
l
−
r
l-r
l−r 的问题暂时简化成单点的问题
我们发现如果把替换建成树的结构的话
虽然这颗树可能很大,但我们可以假定它已经建了出来
我们考虑如何缩小这棵树的大小
可以发现这棵树上有很多的子树是同构的,可以考虑这些子树只记录一次即可
我们发现在
t
t
t 次询问后字符
c
c
c 可以变成的序列是确定的
那么可以记
n
e
[
t
]
[
c
]
ne[t][c]
ne[t][c] 表示在第
t
t
t 次询问时字母是
c
c
c,下一次改变
c
c
c 的值会在何处
n
e
[
t
]
[
c
]
ne[t][c]
ne[t][c] 可以通过倒序预处理出来
所以我们可以间接地建出这张图
然后以同样的方法预处理出
s
u
m
[
t
]
[
c
]
sum[t][c]
sum[t][c] 就可以在
O
(
n
)
O(n)
O(n) 的时间内求出第
k
k
k 个点变化后的字符
转化成
[
l
,
r
]
[l,r]
[l,r] 的问题,可以发现是连续的,那么在树上的叶子也是连续的
然后可以一个一个输出
上面的方法在节点有分叉的时候是没有错的,因为一个分叉至少会带来一个叶子
但我们发现可能会有这样的情况:
有一条很长的链会导致运行很多次都不能添加新的解
这里可以在运行完第一次这条链后就把这条链上每个点连到链的最后一个点
相当于等价代换树的结构
时间复杂度大概是预处理的
O
(
26
n
)
O(26n)
O(26n)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(200100),inf(2e18);
int now,r,n,len[N];
int last[N],ne[N][26],sum[N][26];
char c[N];
string s[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void print(int dep,int c,int tot){
if(dep>n){
now++,putchar(c+'a');
if(now>r) exit(0);
return;
}
if(len[dep]==1){
int _ne=ne[dep][s[dep][0]-'a'];
print(_ne,s[dep][0]-'a',tot);
if(len[_ne]==1){
s[dep][0]=s[_ne][0];
ne[dep][s[dep][0]-'a']=ne[_ne][s[dep][0]-'a'];
}
}
for(int i=0;i<len[dep];i++){
int _c=s[dep][i]-'a';
int w=min(inf,tot+sum[ne[dep][_c]][_c]);
while(w>=now) print(ne[dep][_c],_c,tot);
tot=w;
}
}
signed main(){
now=read(),r=read(),n=read();
for(int i=1;i<=n;i++)
cin>>c[i]>>s[i],len[i]=s[i].size();
for(int i=0;i<26;i++) last[i]=n+1,sum[n+1][i]=1ll;
for(int i=n;i;i--){
for(int j=0;j<26;j++)
ne[i][j]=last[j];
last[c[i]-'a']=i;
}
for(int i=n;i;i--){
for(int j=0;j<26;j++)
sum[i][j]=sum[i+1][j];
sum[i][c[i]-'a']=0;
for(int j=0;j<len[i];j++)
sum[i][c[i]-'a']=min(inf,sum[i][c[i]-'a']+sum[i+1][s[i][j]-'a']);
}
print(last[0],0,0);
return 0;
}