luogu P3975 [TJOI2015]弦论
当 \(t=0\) 时:考虑 \(dp[i]\) 表示到了后缀自动机上的点 \(i\),往后走还能形成多少子串。转移很显然:\(dp[i]=1+\sum dp[nxt_i]\)。加一是因为可以直接停下,\(nxt_i\) 代表 \(i\) 到 \(nxt_i\) 有一条边。然后想主席树之类的查询就可以了。
当 \(t=1\) 时:转移方程变化一下:\(dp[i]=appear[i]+\sum dp[nxt_i]\)。\(appear[i]\) 表示状态 \(i\) 在原串中出现的次数。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2000000;
int n,fuck[N],cnm;
char s[N];
struct Suffix_DFA
{
int last,Siz,head[N],ans[N],cnt,ans1[N];
struct SAM
{
int link,ch[30],len,end;
}sam[N];
struct Edge
{
int nxt,to;
}g[N*2];
void add(int from,int to)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void SAM_Extend(int k)
{
int cur=++Siz;
sam[cur].len=sam[last].len+1,sam[cur].end=1;
int p=last;
while(p!=-1&&!sam[p].ch[k])
sam[p].ch[k]=cur,
p=sam[p].link;
if(p==-1)
sam[cur].link=0;
else
{
int q=sam[p].ch[k];
if(sam[q].len==sam[p].len+1)
sam[cur].link=q;
else
{
int clone=++Siz;
sam[clone].len=sam[p].len+1;
sam[clone].link=sam[q].link;
for (int i=0;i<26;i++)
sam[clone].ch[i]=sam[q].ch[i];
while(p!=-1&&sam[p].ch[k]==q)
sam[p].ch[k]=clone,
p=sam[p].link;
sam[q].link=sam[cur].link=clone;
}
}
last=cur;
}
void dfs(int x)
{
for (int i=head[x];i;i=g[i].nxt)
dfs(g[i].to),
sam[x].end+=sam[g[i].to].end;
}
void DP(int x)
{
ans[x]=1,ans1[x]=sam[x].end;
for (int i=0;i<26;i++)
{
int v=sam[x].ch[i];
if(v==0) continue;
if(ans[v]!=-1) ans[x]+=ans[v],ans1[x]+=ans1[v];
else DP(v),ans[x]+=ans[v],ans1[x]+=ans1[v];
}
}
int work()
{
sam[0].link=-1;
for (int i=1;i<=n;i++)
SAM_Extend(s[i]-'a');
for (int i=1;i<=Siz;i++)
add(sam[i].link,i);
dfs(0);
memset(ans,-1,sizeof(ans));
DP(0);
}
int calc(int x)
{
return ans[x];
}
int calc1(int x)
{
return ans1[x];
}
void Query(int k,int x,int t)
{
int now=t?sam[x].end:1;
if(x==0) now=0;
if(k<=now) return;
for (int i=0;i<26;i++)
{
int v=sam[x].ch[i];
if(v==0) continue;
if(t==0)
{
if(k-now>calc(sam[x].ch[i]))
now+=calc(sam[x].ch[i]);
else
{
fuck[++cnm]=i;
Query(k-now,sam[x].ch[i],t);
break;
}
}
else
{
if(k-now>calc1(sam[x].ch[i]))
now+=calc1(sam[x].ch[i]);
else
{
fuck[++cnm]=i;
Query(k-now,sam[x].ch[i],t);
break;
}
}
}
}
}S;
void init()
{
scanf("%s",s+1);
n=strlen(s+1);
}
void work()
{
S.work();
int t,x;
scanf("%d %d",&t,&x);
S.ans1[0]-=S.sam[0].end,S.ans[0]--;
if((t?S.ans1[0]:S.ans[0])<x)
{
puts("-1");
return;
}
S.ans[0]=S.ans1[0]=S.sam[0].end=0;
S.Query(x,0,t);
for (int i=1;i<=cnm;i++)
printf("%c",fuck[i]+'a');
}
int main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!