[TJOI2015]弦论

description

题面

求给定字符串的第\(k(k\le 10^9)\)大子串。无解输出-1,\(n\le 5\times 10^5\)
分不同位置的相同子串算作一个,不同位置的相同子串算作多个两种情况

solution

我们首先要建立一个\(SAM\)...

由于\(SAM\)相当于一个放了当前字符串的所有子串的\(AC\)自动机,
因此我们可以直接在\(SAM\)上进行查找

如果不同位置的相同子串算作一个,那么我们实际上要做的就是查找本质不同的第\(k\)大子串;
\(sum_i\)表示当前查找到第\(i\)号节点后,从该节点出发能找到能被\(SAM\)识别的多少子串(包括空串)
求的时候只要把节点按照\(len_i\)拍个序,然后按照\(SAM\)的转移边像树形\(DP\)处理\(sz\)一样处理即可

for(RG int i=1;i<=tot;i++)t[len[i]]++;
for(RG int i=1;i<=tot;i++)t[i]+=t[i-1];
for(RG int i=1;i<=tot;i++)a[t[len[i]]--]=i;
//这里是基数排序
for(RG int i=1;i<=tot;i++)sum[i]=sz[i]=1;

sz[1]=sum[1]=0;
for(RG int i=tot;i;i--)
    for(RG int j=0;j<26;j++)
        sum[a[i]]+=sum[tr[a[i]][j]];

如果不同位置的相同子串算作多个,那么我们肯定要处理出\(endpos_i(right_i)\)的大小(\(sz\)):

void extend(int c){
    ...
    sz[u]=1;
}
for(RG int i=1;i<=tot;i++)t[len[i]]++;
for(RG int i=1;i<=tot;i++)t[i]+=t[i-1];
for(RG int i=1;i<=tot;i++)a[t[len[i]]--]=i;
for(RG int i=tot;i;i--)sz[fa[a[i]]]+=sz[a[i]];

sz[1]=sum[1]=0;
for(RG int i=tot;i;i--)
    for(RG int j=0;j<26;j++)
        sum[a[i]]+=sum[tr[a[i]][j]];

然后向上面一样处理出\(sum\)即可

code

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<ctime>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define FILE "a"
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e9+7;
const int N=1000010;
const dd pi=acos(-1);
const int inf=2147483647;
const ll INF=1e18+1;
il ll read(){
    RG ll data=0,w=1;RG char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    return data*w;
}

il void file(){
    srand(time(NULL)+rand());
    freopen(FILE".in","r",stdin);
    freopen(FILE".out","w",stdout);
}

char s[N];
int n,b,k,tr[N][26],fa[N],sz[N],len[N],sum[N],a[N],t[N],tot=1,lst=1;
il void extend(int c){
    RG int u=++tot,v=lst;lst=u;len[u]=len[v]+1;
    while(v&&!tr[v][c])tr[v][c]=u,v=fa[v];
    if(!v)fa[u]=1;
    else{
        RG int x=tr[v][c];
        if(len[x]==len[v]+1)fa[u]=x;
        else{
            RG int y=++tot;len[y]=len[v]+1;
            memcpy(tr[y],tr[x],sizeof(tr[y]));
            fa[y]=fa[x];fa[x]=fa[u]=y;
            while(v&&tr[v][c]==x)tr[v][c]=y,v=fa[v];
        }
    }
    sz[u]=1;
}

il void search(int p,int x,int k){
    if(sum[x]<k){puts("-1");exit(0);}
    for(RG int i=0;i<26;i++)
        if(tr[x][i]){
            if(k<=sum[tr[x][i]]){
                s[p]=i+'a';
                if(k<=sz[tr[x][i]]){
                    s[p+1]=0;
                    printf("%s\n",s+1);
                    exit(0);
                }
                else search(p+1,tr[x][i],k-sz[tr[x][i]]);
            }
            else k-=sum[tr[x][i]];
        }
}

int main()
{
    scanf("%s",s+1);n=strlen(s+1);b=read();k=read();
    for(RG int i=1;i<=n;i++)extend(s[i]-'a');
    //Case 1
    
    for(RG int i=1;i<=tot;i++)t[len[i]]++;
    for(RG int i=1;i<=tot;i++)t[i]+=t[i-1];
    for(RG int i=1;i<=tot;i++)a[t[len[i]]--]=i;
    for(RG int i=tot;i;i--)sz[fa[a[i]]]+=sz[a[i]];
    for(RG int i=1;i<=tot;i++)sum[i]=sz[i]=(b?sz[i]:1);
    sz[1]=sum[1]=0;
    
    for(RG int i=tot;i;i--){
        for(RG int j=0;j<26;j++)
            sum[a[i]]+=sum[tr[a[i]][j]];
        //printf("sum[%d]=%d,sz[%d]=%d\n",a[i],sum[a[i]],a[i],sz[a[i]]);
    }
    //Case 2
    
    search(1,1,k);
    //Case 3
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cjfdf/p/9362713.html