本文移植自个人的原博客(原文

AC自动机(简单版)

题目链接

Luogu 3808

题解

AC自动机裸题,如果不加$last$优化会TLE。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+1;

int ch[N][26],f[N],cnt;
int l[N];
int tag[N];

void add(char *s){
int n=strlen(s);
int x=0;
for(int i=0;i<n;++i){
int c=s[i]-'a';
if(ch[x][c]==-1){
ch[x][c]=++cnt;
for(int j=0;j<26;++j)ch[cnt][j]=-1;
}
x=ch[x][c];
}
tag[x]++;
}

void getfail(){
queue<int>q;
for(int i=0;i<26;++i)
if(~ch[0][i])l[ch[0][i]]=f[ch[0][i]]=0,q.push(ch[0][i]);
else ch[0][i]=0;
// static int g[N];
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i){
if(tag[ch[f[x]][i]])l[ch[x][i]]=ch[f[x]][i];
else l[ch[x][i]]=l[ch[f[x]][i]];
if(~ch[x][i])f[ch[x][i]]=ch[f[x]][i],q.push(ch[x][i]);
else ch[x][i]=ch[f[x]][i];
}
}
}
bool vis[N];
int get(char *s){

int n=strlen(s);
int x=0,ret=0;
for(int i=0;i<n;++i){
x=ch[x][s[i]-'a'];
for(int j=x;j;j=l[j])
if(!vis[j])vis[j]=1,ret+=tag[j];
}
return ret;
}

int n;
char s[N];
int main(){
// freopen("c.in","r",stdin);
cin>>n;
for(int i=0;i<26;++i)ch[0][i]=-1;
for(int i=0;i<n;++i)scanf("%s",s),add(s);
getfail();
scanf("%s",s);
cout<<get(s)<<endl;
}

AC自动机(加强版)

题目链接

Luogu 3796

题解

AC自动机裸题。不加$last$优化也能过,多组数据注意清零。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+1;

int tr[N][26],f[N],cnt,ans[N];
int l[N];
int tag[N];

void add(char *s,int fa){
int n=strlen(s);
int x=0;
for(int i=0;i<n;++i){
int c=s[i]-'a';
if(tr[x][c]==-1){
tr[x][c]=++cnt;
for(int j=0;j<26;++j)tr[cnt][j]=-1;
}
x=tr[x][c];
}
tag[x]=fa;
}

void getfail(){
queue<int>q;
for(int i=0;i<26;++i)
if(~tr[0][i])l[tr[0][i]]=f[tr[0][i]]=0,q.push(tr[0][i]);
else tr[0][i]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i){
if(tr[x][i]==-1){tr[x][i]=tr[f[x]][i];continue;}
int y=tr[x][i];
q.push(y);
f[y]=tr[f[x]][i];
// l[y]=tag[f[y]]?f[y]:l[f[y]];
}
}
}
bool vis[N];

void get(char *s){

int n=strlen(s);
int x=0,ret=0;
for(int i=0;i<n;++i){
x=tr[x][s[i]-'a'];
for(int j=x;j;j=f[j])
if(tag[j])ans[tag[j]]++;
}
}

int n;
char s[256][256];
char s1[N];

void put(char *s){
int n=strlen(s);
for(int i=0;i<n;++i)putchar(s[i]);
printf("\n");
}

int main(){
while(scanf("%d",&n)!=EOF){
if(!n)return 0;
cnt=0;
for(int i=0;i<26;++i)tr[0][i]=-1;
for(int i=1;i<=n;++i)scanf("%s",s[i]),add(s[i],i);
getfail();
scanf("%s",s1);get(s1);
int res=*max_element(ans+1,ans+n+1);
printf("%d\n",res);
for(int i=1;i<=n;++i)if(ans[i]==res)put(s[i]);
for(int i=1;i<=n;++i)ans[i]=0;
for(int i=1;i<=cnt;++i)tag[i]=f[i]=l[i]=0;
}
}

HNOI2006 最短母串

题目链接

Luogu 2322

题解

考虑$n$很小的性质,第一眼以为是状态压缩DP暴力转移(实际也能做,但是很难写),正解应该是将在AC自动机上DP。根据AC自动机的性质,易知一条路径即代表一个字符串。设$f[i][S]$表示当前走到第$i$号节点,已经覆盖串的集合为$S$的最小步数状态数为$50n2^n$,直接用BFS更新即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 6e2+5;

int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
}

char s[13][51];
int n;
struct kd{
int x,y;
}X,Y;

struct AC{
int cnt,tr[N][26],f[N],tag[N],pos[N];
int d[N][1<<12];
int prex[N][1<<12],prey[N][1<<12];

void init(){
cnt=0;
memset(tr[0],-1,sizeof(tr[0]));
}

void add(char *s,int k){
int n=strlen(s);
int x=0;
for(int i=0;i<n;++i){
int c=s[i]-'A';
if(tr[x][c]==-1){
tr[x][c]=++cnt;
pos[cnt]=c;
memset(tr[cnt],-1,sizeof(tr[cnt]));
tag[cnt]=0;
}
x=tr[x][c];
}
tag[x]|=1<<k;
}

void getf(){
queue<int>q;
f[0]=0;
for(int i=0;i<26;++i)
if(~tr[0][i])f[tr[0][i]]=0,q.push(tr[0][i]);
else tr[0][i]=0;
while(!q.empty()){
int x=q.front();q.pop();
tag[x]|=tag[f[x]];
for(int i=0;i<26;++i){
if(~tr[x][i])f[tr[x][i]]=tr[f[x]][i],q.push(tr[x][i]);
else tr[x][i]=tr[f[x]][i];
}
}
}

void output(int x,int y){
static int q[N];
while(x){
q[++q[0]]=pos[x]+'A';
int x1=x,y1=y;
x=prex[x1][y1];
y=prey[x1][y1];
}
reverse(q+1,q+q[0]+1);
for(int i=1;i<=q[0];++i)printf("%c",(char)q[i]);
}

void solve(){
getf();
int S=(1<<n)-1;
memset(d,-1,sizeof(d));
queue<kd>q;
X.x=0;X.y=0;
q.push(X);d[0][0]=0;
prex[0][0]=0;prey[0][0]=0;
while(!q.empty()){
X=q.front();q.pop();
for(int i=0;i<26;++i)
if(tr[X.x][i]){
int to=tr[X.x][i];
Y.x=to;Y.y=X.y|tag[to];
if(d[Y.x][Y.y]==-1){
prex[Y.x][Y.y]=X.x;prey[Y.x][Y.y]=X.y;

d[Y.x][Y.y]=d[X.x][X.y]+1;
if(Y.y==S){
// printf("%d\n",d[Y.x][Y.y]);
output(Y.x,Y.y);
printf("\n");
exit(0);
}
q.push(Y);
}
}
}
}
}T;

int main(){
scanf("%d",&n);
T.init();
for(int i=0;i<n;++i){
scanf("%s",s[i]);
T.add(s[i],i);
}
T.solve();
}

BJOI2017 魔法咒语

题目链接

Luogu 3715

题解

设$f[i][L]$为走到第$i$号节点,此时字符串长度(即路径长度)为$L$的方案数,那么由于字符集在此题中被限制为了一个个的字符串,所以需要预处理$go[i][j]$表示从$i$号节点出发后经过第$j$个基本词汇后所到达的节点,如果其中经过禁忌词语,则初始化为$-1$表示不可到达。那么DP方程如下:($len[j]$表示基本词汇$j$的长度)

$$f[go[i][j]][L+len[j]] += f[i][L] (go[i][j]!=-1)$$

观察数据范围,朴素的DP只能通过$60 \ percents$的数据,剩余$L<=10^8$且$len[j]<=2$的部分分,可以通过矩阵乘法优化,用$f[i][j]$表示从$i$号节点一步到达$j$号节点的方案数,可以处理$len[j]=1$的情况。对于$len[j]=2$的情况,对于每个节点$i$设一个虚拟节点$b[i]$,然后$f[i][b[j]]$表示从$i$一步到$b[j]$的方案数,再从$b[j]$向$j$连一条边即可(即f$[b[j]][j]=1$)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e3+1;
const ll mod = 1e9+7;
int n,m,L,cnt,len[51];
char a[51][401],b[51][401];

void mul(int &a,int b){
a+=b;
if(a>=mod)a-=mod;
}
int t1k;
struct Matrix{
ll a[401][401];
friend Matrix operator * (Matrix a,Matrix b){
Matrix c;
memset(c.a,0,sizeof(c.a));
for(int i=0;i<=t1k;++i)
for(int j=0;j<=t1k;++j){
for(int k=0;k<=t1k;++k)
(c.a[i][j]+=a.a[i][k]*b.a[k][j])%=mod;
}
return c;
}
}res,c;



struct AC{
int tr[N][26],f[N],tag[N],val[N],to[N][51];

void init(){
cnt=0;
memset(tr[0],-1,sizeof(tr[0]));
}

void add(char *s,int h){
int x=0;
int n=strlen(s);
for(int i=0;i<n;++i){
int c=s[i]-'a';
if(tr[x][c]==-1){
tr[x][c]=++cnt;
val[cnt]=i;
memset(tr[cnt],-1,sizeof(tr[cnt]));
}
x=tr[x][c];
}
tag[x]=h;
}

void getf(){
queue<int>q;
for(int i=0;i<26;++i)
if(~tr[0][i])f[tr[0][i]]=0,q.push(tr[0][i]);
else tr[0][i]=0;
while(!q.empty()){
int x=q.front();q.pop();
tag[x]|=tag[f[x]];
for(int i=0;i<26;++i)
if(~tr[x][i])f[tr[x][i]]=tr[f[x]][i],q.push(tr[x][i]);
else tr[x][i]=tr[f[x]][i];
}
}

int go(int i,char *s){
int n=strlen(s);
int x=i;
if(tag[x])return -1;
for(int j=0;j<n;++j){
x=tr[x][s[j]-'a'];
if(tag[x])return -1;
}
return x;
}

void A(){
static int dp[N][101];
dp[0][0]=1;
for(int l=0;l<=L;++l)
for(int i=0;i<=cnt;++i)
for(int j=0;j<n;++j)
if(len[j]+l<=L){
int y=to[i][j];
if(y==-1)continue;
mul(dp[y][len[j]+l],dp[i][l]);
}
int ans=0;
for(int i=0;i<=cnt;++i)mul(ans,dp[i][L]);
printf("%d\n",ans);
}

void B(){
t1k=2*cnt+1;
int tp=cnt+1;
for(int i=0;i<=cnt;++i)res.a[i+tp][i]=1;
for(int i=0;i<=2*cnt+1;++i)c.a[i][i]=1;
for(int i=0;i<=cnt;++i)
for(int j=0;j<n;++j){
int y=to[i][j];
if(y==-1)continue;
if(len[j]==1)res.a[i][y]++;
else res.a[i][y+tp]++;
}
while(L){
if(L&1)c=c*res;
L>>=1;
res=res*res;
}
ll ans=0;
for(int i=0;i<=cnt;++i)(ans+=c.a[0][i])%=mod;
printf("%lld\n",ans);
}

void solve(){
init();
for(int i=0;i<m;++i)add(b[i],1);
getf();
for(int i=0;i<=cnt;++i)
for(int j=0;j<n;++j)
to[i][j]=go(i,a[j]);
if(L<=100)A();
else B();
}
}T;

int main(){
// freopen("c.in","r",stdin);
scanf("%d%d%d",&n,&m,&L);
for(int i=0;i<n;++i)scanf("%s",a[i]),len[i]=strlen(a[i]);
for(int i=0;i<m;++i)scanf("%s",b[i]);
T.solve();
}
/*
4 2 98
bo
oo
hm
ba
ob
mho
*/

BJWC2011 禁忌

题目链接

Luogu 4569

题解

首先考虑对于一个给定的串,如何求其禁忌伤害,这是一个经典的贪心问题,即线段覆盖问题,正确的姿势是按右端点排序,能取的尽量取即可,如果去除存在覆盖的禁忌串(一定没有只取被覆盖的子串优越),那么姿势等价于按左端点排序,且尽可能的取。

考虑在AC自动机上的贪心,即为从根节点出发每次走到一个禁忌串后,直接返回根节点(因为禁忌串之间不能相互覆盖),重复过程直到路径长度为$L$并将答案加上返回次数除以$2^{len}$,考虑如何用DP优化此过程,仍然设$f[i][L]$表示走到$i$号节点,路径长度为$L$的概率,那么显然有($0$表示根节点,$tag$表示是否为禁忌串。)

$$f[0][L+1]+=f[i][L]+1/alphabet \ (tag[tr[x][i]]=1)$$

$$f[tr[x][i]][L+1]+=f[i][L]+1/alphabet \ (tag[tr[x][i]]=0)$$

考虑如何将统计答案。在贪心的过程中,每次访问到禁忌串时不仅要返回根节点,还要将答案加上到当前节点的概率,即新建一个节点$g$表示答案节点,那么同样有

$$f[g][L+1]+=f[i][L]+1/alphabet \ (tag[tr[x][i]]=1)$$

即可以用矩阵乘法优化此过程,并且需要设定$matrix[g][g]=1$,矩阵$L$次方代表$1−L$前缀和的贡献,即为答案。

#include<bits/stdc++.h>
#define mp make_pair
#define fr first
#define sd second
#define pii pair<int,int>
using namespace std;
typedef long long ll;
typedef long double db;
const int N = 1e2+1;

int cnt,n,L,ml,len[6],tr[N][26],f[N],tag[N];
char s[6][N];

struct matrix{

db a[N][N];

friend matrix operator * (matrix a,matrix b){
matrix c;
for(int i=0;i<=cnt+1;++i)
for(int j=0;j<=cnt+1;++j)
c.a[i][j]=0;

for(int i=0;i<=cnt+1;++i)
for(int j=0;j<=cnt+1;++j)
for(int k=0;k<=cnt+1;++k)
c.a[i][j]+=a.a[i][k]*b.a[k][j];
return c;
}

}res;

matrix qpow(ll b){
matrix c;
for(int i=0;i<=cnt+1;++i)
for(int j=0;j<=cnt+1;++j)
c.a[i][j]=(i==j);

while(b){
if(b&1)c=c*res;
b>>=1;
res=res*res;
}
return c;
}

void solve(){
db k=1.00/(db)ml;
for(int i=0;i<=cnt;++i)
for(int j=0;j<ml;++j){
int y=tr[i][j];
if(tag[y])res.a[i][0]+=k,res.a[i][cnt+1]+=k;
else res.a[i][y]+=k;
}
res.a[cnt+1][cnt+1]=1;
printf("%.8lf",(double)qpow(L).a[0][cnt+1]);
}

int add(char *s){
int n=strlen(s),x=0;
for(int i=0;i<n;++i){
int c=s[i]-'a';
if(tr[x][c]==-1){
tr[x][c]=++cnt;
memset(tr[cnt],-1,sizeof(tr[cnt]));
}
x=tr[x][c];
}
tag[x]=1;
return n;
}

void getf(){
queue<int>q;
for(int i=0;i<ml;++i)
if(~tr[0][i])f[tr[0][i]]=0,q.push(tr[0][i]);
else tr[0][i]=0;
while(!q.empty()){
int x=q.front();q.pop();
tag[x]|=tag[f[x]];
for(int i=0;i<ml;++i){
if(~tr[x][i])f[tr[x][i]]=tr[f[x]][i],q.push(tr[x][i]);
else tr[x][i]=tr[f[x]][i];
}
}
}

int main(){
// freopen("c.in","r",stdin);
memset(tr[0],-1,sizeof(tr[0]));
scanf("%d%d%d",&n,&L,&ml);
for(int i=1;i<=n;++i)scanf("%s",s[i]),len[i]=add(s[i]);
getf();
solve();
}

SCOI2012 喵星球上的点名

题目链接

Luogu 2336

题解

由于本题中字符集过大,肯定不能将数组开满,需要用$map$来存储儿子,且不能在每次新建节点时将儿子都表示成$−1$,那么就要默认为$0$。同理需要将根节点设为$1$,并赋值$tr[0][i]=1$,在$getfail$的时候,也应该直接暴力跳$fail$。由于数据较水,这样即可通过此题。(正解为后缀数组+主席树)

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N = 1e5+5;

map<int,int>tr[N];
map<int,int>::iterator it;
int n,m,L,cnt,f[N],g[N],ans[N],res[N],id[N];
int vis[N],vis1[N],T;
vector<int>a[N],b[N],tag[N];

int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}

void add(int *s,int n,int fa){
int x=1;
for(int i=1;i<=n;++i){
int c=s[i];
if(!tr[x][c])tr[x][c]=++cnt;
x=tr[x][c];
}
tag[x].pb(fa);
}

void getf(){
queue<int>q;
q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
for(it=tr[x].begin();it!=tr[x].end();++it){
int t=it->first,k=f[x];
while(!tr[k][t])k=f[k];
f[it->second]=tr[k][t];
q.push(it->second);
}
// for(int i=0;i<L;++i){
// if(tr[x][i])f[tr[x][i]]=tr[f[x]][i],q.push(tr[x][i]);
// else tr[x][i]=tr[f[x]][i];
// }
}
}

int cala(int g){
int x=1,ret=0;
for(int i=0;i<a[g].size();++i){
int to=a[g][i];
while(!tr[x][to])x=f[x];x=tr[x][to];
for(int j=x;j;j=f[j]){
if(vis1[j]==T)break;vis1[j]=T;
for(int k=0;k<tag[j].size();++k){
int y=tag[j][k];
if(vis[y]!=T)vis[y]=T,++ret,ans[y]++;
}
}
}
return ret;
}

int calb(int g){
int x=1,ret=0;
for(int i=0;i<b[g].size();++i){
int to=b[g][i];
while(!tr[x][to])x=f[x];x=tr[x][to];
for(int j=x;j;j=f[j]){
if(vis1[j]==T)break;vis1[j]=T;
for(int k=0;k<tag[j].size();++k){
int y=tag[j][k];
if(vis[y]!=T)vis[y]=T,++ret,ans[y]++;
}
}
}
return ret;
}

int main(){
// freopen("c.in","r",stdin);
L=10001;cnt=1;f[1]=0;
for(int i=0;i<L;++i)tr[0][i]=1;
n=read();m=read();
for(int i=1,k;i<=n;++i){
k=read();for(int j=1;j<=k;++j)a[i].pb(read());
k=read();for(int j=1;j<=k;++j)b[i].pb(read());
}
for(int i=1,k;i<=m;++i){
k=read();for(int j=1;j<=k;++j)g[j]=read();
add(g,k,i);
}
getf();
for(int i=1;i<=n;++i){
++T;
res[i]=
cala(i)+
calb(i);
}
for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
for(int i=1;i<=n;++i){
printf("%d",res[i]);
if(i!=n)printf(" ");
}
return 0;
}

HAOI2017 字符串

题目链接

Luogu 3735

题解

对于每个询问串和初始串,将问题转化为其前后缀的匹配(初始串的表示为$x,y$询问串的表示为为$a,b$)。先将所有询问串建到AC自动机上,然后建出$fail$树。

同$NOI2011$阿狸的打字机一样,对于一个初始串的子串能匹配到询问串当且仅当在$fail$树中,$x[j]$在$a[j]$的子树中,且$y[j+k+1]$在$b[j+k+1]$的子树中,该算法可以通过子树差分来实现。但是如果直接统计所有前后缀的话,明显会存在重复的情况:

初始串:$aaab$

询问串:$abab$

$k=2$

此时显然询问串与初始串匹配了两回(即$x[0]−>y[3]$与$a[0]−>b[3]$和$x[1]−>y[4]$与$a[1]−>b[4]$)。

一般性的,以上算法实际上求的是$k1 \in [1,k]$中所有可以匹配的$k1$的个数,那么$k$匹配个数即为$[1,k]$匹配个数与$[1,k−1]$匹配个数的差。(需要注意在求解$[1,k−1]$的时候,$a,b,x,y$的边界条件需要与求解$[1,k]$时对齐)

#include<bits/stdc++.h>
#define mp make_pair
#define pii pair<int,int>
#define fr first
#define pb push_back
#define sd second
using namespace std;
typedef long long ll;
const int N = 4e5+5;

int k;
char s[N],p[N];
int n,ans[N];
vector<pii>sum1[N],sum2[N];
vector<int>g[N],ch1[N],ch2[N];
int st[N],ed[N],totw;

struct lb{
int a[N];
lb(){
memset(a,0,sizeof(a));
}
void add(int x){
for(;x<=totw;x+=x&-x)a[x]++;
}
int sum(int x){
int ret=0;
for(;x;x-=x&-x)ret+=a[x];
return ret;
}
int get(int x){
return sum(ed[x])-sum(st[x]-1);
}
}T[2];

void _debug(int *a){
for(int i=0;a[i];++i)printf("%d ",a[i]);
cout<<endl;
}

struct __AC{
int tr[N][95],f[N],cnt,x[N],y[N];
void add(char *s,int n,int *q,bool flag){
int x=0;
q[0]=q[n+1]=0;
for(int i=flag?n:1;flag?i>=1:i<=n;flag?--i:++i){
int c=s[i]-33;
if(tr[x][c]==-1){
tr[x][c]=++cnt;
memset(tr[cnt],-1,sizeof(tr[cnt]));
}
q[i]=x=tr[x][c];
}
}
void getf(){
queue<int>q;
for(int i=0;i<95;++i)
if(~tr[0][i])q.push(tr[0][i]);
else tr[0][i]=0;
while(!q.empty()){
int x=q.front();q.pop();
g[f[x]].pb(x);
for(int i=0;i<95;++i)
if(~tr[x][i])f[tr[x][i]]=tr[f[x]][i],q.push(tr[x][i]);
else tr[x][i]=tr[f[x]][i];
}
}
void dfs(int x){
st[x]=++totw;
for(int i=0;i<g[x].size();++i)dfs(g[x][i]);
ed[x]=totw;
}
void getans(int x){
for(int i=0;i<sum1[x].size();++i){pii t=sum1[x][i];ans[t.sd]-=T[0].get(t.fr);}
for(int i=0;i<sum2[x].size();++i){pii t=sum2[x][i];ans[t.sd]+=T[1].get(t.fr);}
for(int i=0;i<ch1[x].size();++i){int t=ch1[x][i];T[0].add(st[t]);}
for(int i=0;i<ch2[x].size();++i){int t=ch2[x][i];T[1].add(st[t]);}
for(int i=0;i<g[x].size();++i)getans(g[x][i]);
for(int i=0;i<sum1[x].size();++i){pii t=sum1[x][i];ans[t.sd]+=T[0].get(t.fr);}
for(int i=0;i<sum2[x].size();++i){pii t=sum2[x][i];ans[t.sd]-=T[1].get(t.fr);}
}
void solve(){
memset(tr[0],-1,sizeof(tr[0]));
int len=strlen(s+1);
for(int i=1;i<=n;++i){
scanf("%s",p+1);
int m=strlen(p+1);
if(m<=k)ans[i]=len-m+1;
else{
add(p,m,x,0);add(p,m,y,1);
// _debug(x);_debug(y);
for(int j=k+1;j<=m+1;++j)sum1[x[j-k-1]].pb(mp(y[j],i));
for(int j=k+1;j<=m;++j)sum2[x[j-k]].pb(mp(y[j],i));
}
}
getf();
int pt;
x[0]=x[len+1]=y[0]=y[len+1]=0;
pt=0;for(int i=1;i<=len;++i)x[i]=pt=tr[pt][s[i]-33];
pt=0;for(int i=len;i>=1;--i)y[i]=pt=tr[pt][s[i]-33];
// _debug(x);_debug(y);
// cout<<endl;
for(int j=k+1;j<=len+1;++j)ch1[x[j-k-1]].pb(y[j]);
for(int j=k+1;j<=len;++j)ch2[x[j-k]].pb(y[j]);
dfs(0);
// _debug(st);
getans(0);
}
}AC;

int main(){
// freopen("problemb.in","r",stdin);
// freopen("problemb.out","w",stdout);
// freopen("c.in","r",stdin);
scanf("%d%s%d",&k,s+1,&n);
AC.solve();
for(int i=1;i<=n;++i)printf("%d\n",ans[i]);
// cout<<AC.cnt<<endl;
}

TJOI2013 单词

COCI2015 Divljak