标签:++ 代码 学习 range str tchar 问题 英文字母 范围
为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?
输入格式:
第一行是一个仅由小写英文字母构成的字符串s
第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。
输出格式:
输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。
对于10%的数据,n ≤ 1000。
对于50%的数据,t = 0。
对于100%的数据,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。
题解:首先题目有两个要求,t=1,不同位置相同子串看做不同;t=0:,不同位置的相同子串看成相同;
先对这个串建个SAM,再对所有节点按照maxlen排下序。这样就可以从长到短地总结答案。
首先对每个节点维护一个当前节点所占的子串个数。假如t=1就按照Parent树里从叶子节点一层一层往上推的顺序,计算每个串出现的次数。t=0就不用管,直接设成1
这样就可以维护出每个节点往后推会有多少个串。。(直接加起来
然后在SAM上按照字典序往下匹配就可以了
参考代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int maxn=1e6+10; char s[maxn]; int k,t; struct SAM{//求字典序第K小串 int l[maxn<<1],fa[maxn<<1],nxt[maxn<<1][26]; int last,cnt,c[maxn<<1],siz[maxn<<1],sum[maxn<<1],a[maxn<<1]; void Init() { memset(siz,0,sizeof(siz)); memset(c,0,sizeof(c)); memset(sum,0,sizeof(sum)); memset(a,0,sizeof(a)); last=cnt=1; memset(nxt[1],0,sizeof(nxt[1])); fa[1]=l[1]=0; } int NewNode() { cnt++; memset(nxt[cnt],0,sizeof(nxt[cnt])); fa[cnt]=l[cnt]=0; return cnt; } void Add(int ch) { int p=last,np=NewNode(); last=np; l[np]=l[p]+1; siz[np]=1; while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=nxt[p][ch]; if(l[q]==l[p]+1) fa[np]=q; else { int nq=NewNode(); memcpy(nxt[nq],nxt[q],sizeof(nxt[q])); fa[nq]=fa[q]; l[nq]=l[p]+1; fa[np]=fa[q]=nq; while(nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p]; } } } void Build() { int len=strlen(s+1); for(int i=1;i<=len;i++) Add(s[i]-‘a‘); } void topusort() { for(int i=1;i<=cnt;++i) c[l[i]]++; for(int i=1;i<=cnt;++i) c[i]+=c[i-1]; for(int i=1;i<=cnt;++i) a[c[l[i]]--]=i; for(int i=cnt;i;--i) {//t==1:不同位置的相同子串视为不同 if(t) siz[fa[a[i]]]+=siz[a[i]]; else siz[a[i]]=1;//t==0:视为相同 } siz[1]=0; for(int i=cnt;i;--i) { sum[a[i]]=siz[a[i]]; for(int j=0;j<26;++j) if(nxt[a[i]][j]) sum[a[i]]+=sum[nxt[a[i]][j]]; } } void dfs() { if(k>sum[1]){puts("-1");return ;} int now=1; while(k>0) { int p=0; while(k>sum[nxt[now][p]]) { k-=sum[nxt[now][p]]; p++; } now=nxt[now][p]; putchar(‘a‘+p); k-=siz[now]; } return ; } } sam; int main() { scanf("%s%d%d",s+1,&t,&k); sam.Init(); sam.Build(); sam.topusort(); sam.dfs(); return 0; }
标签:++ 代码 学习 range str tchar 问题 英文字母 范围
原文地址:https://www.cnblogs.com/songorz/p/10805198.html