bzoj 3998 弦论 —— 后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3998
关于相同子串算一个还是算多个,其实就是看一种状态的 right 集合是否加上 Parent 树中子树的 right 集合;
虽然是子串却不管 l 数组,因为实际上 dfs 走到一个点,得到的是一个确定的子串,而这个子串的状态属于这个点表示的状态,l 数组是这个点表示的状态数,当然不用考虑;
记一个 sum 表示这个状态往后加字母能得到的所有子串个数,然后在SAM上按字典序 dfs ,走过的边就是添加的字符。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=5e5+5; int T,K,fa[xn<<1],l[xn<<1],go[xn<<1][30],lst=1,cnt=1,siz[xn<<1],sum[xn<<1]; int tax[xn<<1],t[xn<<1]; char s[xn]; void add(int w) { int p=lst,np=++cnt; lst=np; l[np]=l[p]+1; siz[np]=1; for(;p&&!go[p][w];p=fa[p])go[p][w]=np; if(!p)fa[np]=1; else { int q=go[p][w]; if(l[q]==l[p]+1)fa[np]=q; else { int nq=++cnt; l[nq]=l[p]+1; memcpy(go[nq],go[q],sizeof go[q]); fa[nq]=fa[q]; fa[q]=fa[np]=nq; for(;go[p][w]==q;p=fa[p])go[p][w]=nq; } } } void Tsort() { for(int i=1;i<=cnt;i++)tax[l[i]]++; for(int i=1;i<=cnt;i++)tax[i]+=tax[i-1]; for(int i=1;i<=cnt;i++)t[tax[l[i]]--]=i; for(int i=cnt;i;i--) { int x=t[i]; if(T==0)siz[x]=1; else siz[fa[x]]+=siz[x]; } siz[1]=0;// for(int i=cnt;i;i--) { int x=t[i]; sum[x]=siz[x]; for(int j=0;j<26;j++) if(go[x][j])sum[x]+=sum[go[x][j]]; } } void dfs(int x,int s) { if(s<=siz[x])return;//<= s-=siz[x]; for(int j=0;j<26;j++) { if(!go[x][j])continue; if(s>sum[go[x][j]])s-=sum[go[x][j]]; else { putchar(j+'a'); dfs(go[x][j],s); return; } } } int main() { scanf("%s",s+1); int n=strlen(s+1); scanf("%d%d",&T,&K); for(int i=1;i<=n;i++)add(s[i]-'a'); Tsort(); dfs(1,K); puts(""); return 0; }