2017.8.21 弦论 思考记录

来源:互联网 发布:淘宝店铺如何转让 编辑:程序博客网 时间:2024/05/01 22:17

自己a的第一道sam  虽然是板子题

要求第k小的串,分两种情况:

一种是包括重复的:

包括重复的即包括right的size,所以每个点向它的父节点贡献自己的size即可,因为这个父指针和ac自动机的适配指针迷之相似,都会指向和自己相同的之前的最近的节点

由于一个节点必须接受完所有子节点的贡献才能往上传,所以需要拓扑序,这个利用step进行计数排序即可(由于第二层的点会新于第一层的点,所以要按step排,

不能简单从cnt倒序)  基数排序是O(n+k)的稳定排序,当然你想用快排或冒泡也没人拦你

包括重复的第二层的点sz就不用设1了,因为它本质上是虚拟节点,会有两个以上节点加过来,

然后dp一下right集合的大小,分进去找即可

不包括重复的:

就很简单了,但注意第二层的节点要设sz=1,因为它们 没有子节点的转移,就独自具有意义了(和下面的点无关,独立)


O(n*26)


码(挺短的):

#include<iostream>#include<cstdio>#include<cstring>using namespace std;#define N 1500009int f[N],cnt=1,last=1,ch[N][27],fu[N],l[N],tot,k,sz[N],t,cc[N],a[N];char str[N],ans[N];void jia(int o){int np=++cnt,p=last;l[np]=l[p]+1,last=np;for(;p&&!ch[p][o];p=fu[p])ch[p][o]=np;if(p==0)fu[np]=1;else{int q=ch[p][o];if(l[q]==l[p]+1)fu[np]=q;       else{int nq=++cnt;l[nq]=l[p]+1;for(int i=0;i<=26;i++)ch[nq][i]=ch[q][i];fu[nq]=fu[q];fu[q]=fu[np]=nq;for(;ch[p][o]==q;p=fu[p])ch[p][o]=nq;if(t==0)sz[nq]=1;}}sz[np]=1;}int dp(int o){if(f[o])return f[o]; for(int i=0;i<=26;i++){if(ch[o][i]==0)continue;f[o]+=dp(ch[o][i]);}f[o]+=sz[o];return f[o];}void xz(int o){for(int i=0;i<=25;i++){if(ch[o][i]==0||k<=0)continue;if(k-f[ch[o][i]]<=0)ans[++tot]=i+'a',k-=sz[ch[o][i]],xz(ch[o][i]);else k-=f[ch[o][i]];}}int main(){scanf("%s",str);scanf("%d%d",&t,&k);int n=strlen(str);for(int i=0;i<n;i++)jia(str[i]-'a');int j;if(t==1){for(j=1;j<=cnt;j++)cc[l[j]]++;for(j=1;j<=cnt;j++)cc[j]+=cc[j-1];for(j=1;j<=cnt;j++)a[cc[l[j]]--]=j;for(int i=cnt;i>=1;i--)sz[fu[a[i]]]+=sz[a[i]];}dp(1);xz(1);if(k>0)printf("-1"); for(int i=1;i<=tot;i++)printf("%c",ans[i]);}



原创粉丝点击