标签:one sizeof scanf hide stdin lap 位置 show +=
[TJOI2015]弦论(sam)
首先建出当前串s的sam。然后分t=1或者0两种情况讨论。
当t=1时,位置不同的相同字符串算不同。
设开头为某一个x对应的字符串的子串个数是$f[x]$,节点x对应的字符串在整个字符串中出现了$s[x]$次。我们可以通过dfs算法在$parent$树上跑算出$f$和$s$。
我们为了查询字典序,需要从起始节点开始跑自动机。设当前节点为x,代表字符串是s,然后由字典序小到大枚举x后面添加的字符c。如果现在以x+c为开头对应的字符串数量比较小,那么把k减去对应的f。否则,x转移到s+c所对应的节点。
注意每次x转移后也要减去$s[x]$。否则由于没有算上x的贡献,会导致答案错误。
#include<bits/stdc++.h> #define mn 1000005 using namespace std; char str[mn];long long ans; int a,k; int fa[mn],t[mn][26],len[mn],la,ct,v[mn],h[mn],nxt[mn],ec,s[mn],f[mn]; void add(int c){ int cur=++ct,p=la;len[cur]=len[la]+1;s[cur]=1;la=cur; for(;p&&!t[p][c];p=fa[p])t[p][c]=cur; if(!p){fa[cur]=1;} else{ int q=t[p][c];if(len[p]+1==len[q]){fa[cur]=q;} else{ int cl=++ct;len[cl]=len[p]+1;memcpy(t[cl],t[q],sizeof(t[cl])); fa[cl]=fa[q];fa[cur]=fa[q]=cl; for(;t[p][c]==q;p=fa[p])t[p][c]=cl; } } } void init(){ct=ec=la=1;} void adj(int x,int y){v[++ec]=y;nxt[ec]=h[x];h[x]=ec;} void bd(){for(int i=2;i<=ct;i++)adj(fa[i],i);} int g1(int x){ for(int i=h[x];i;i=nxt[i]) s[x]+=g1(v[i]); return s[x]; } int g2(int x){ if(f[x])return f[x]; if(a)f[x]=s[x]; else f[x]=1; for(int i=0;i<26;i++) if(t[x][i])f[x]+=g2(t[x][i]); return f[x]; } int main(){ freopen("string.in","r",stdin); freopen("string.out","w",stdout); scanf("%s%d%d",str+1,&a,&k);init(); for(int i=1;str[i];i++)add(str[i]-‘a‘); bd();g1(1);g2(1); int x=1; while(k>0){ for(int i=0;i<26;i++){ int v=t[x][i]; if(k>f[v])k-=f[v]; else{ x=v; if(!a)k--; else k-=s[v]; printf("%c",i+‘a‘); break; } } } }
标签:one sizeof scanf hide stdin lap 位置 show +=
原文地址:https://www.cnblogs.com/rilisoft/p/11370460.html