GYM 101086 C.Everything(字典树)

Description
类似everything的搜索功能,所有字符串按字典序升序排,每次键入一个前缀光标就会移动到以该前缀为前缀的所有字符串中字典序最小的那个上面,每次键入一个字符串算一次操作,按一下上下键也算一次操作,给出n个串,问在everything中找到该串至少需要几步操作
Input
第一行一整数T表示用例组数,每组用例首先输入字符串数量n,之后n个全由小写字母组成的字符串,每个串串长不超过1e5,总串长不超过5e5(1<=T<=100,1<=n<=1e5)
Output
对于每组用例,输出n个整数,第i个数表示查询到第i个字符串至少几步操作
Sample Input
2
5
scpc
acm
syria
acpc
accepted
5
feglathegreat
candidatesamerraed
masterhossamyousef
candidateabdullahbahosain
masterhassanalhamsh
Sample Output
2 2 2 3 1
2 2 2 1 2
Solution
把所有串插入字典树中,对于每个串s的查询,设s串长为m,在字典树上dfs,不断增加要键入的前缀长度,对于每个前缀的最后一个字符x,有两种情况,一种是就键入x然后往下找,另一种是键入x+1然后往上找,所以在dfs的过程中要维护当前缀长度为i时字典序在s[1,i]前面的串数pre[i]和后面的串数next[i],已经键入长度为i的前缀时,往下找要找pre[m]-pre[i],故总操作数是i+pre[m]-pre[i],或者再键入s[i+1]+1然后往上找,往上要找next[m]-next[i],此时总操作数是i+1+next[m]-next[i],每层都拿这两个值更新一个最小值即可
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 555555
int T,n,no,ch[maxn][26],id[maxn],cnt[maxn],sum[maxn],ans,Pre,Next;
char c[maxn],s[maxn];
void init()
{
    memset(ch[0],-1,sizeof(ch[0]));
    cnt[0]=0,id[0]=-1,no=0;
}
void insert(char *s,int _id)
{
    int u=0;
    cnt[0]++;
    for(int i=0;s[i];i++)
    {
        int c=s[i]-'a';
        if(ch[u][c]==-1)
        {
            ch[u][c]=++no;
            memset(ch[no],-1,sizeof(ch[no]));
            cnt[no]=0,id[no]=-1; 
        }
        u=ch[u][c];
        cnt[u]++;
    }
    id[u]=_id;
}
void dfs(int l,int r,int u,int pre,int next,int deep)
{
    if(l==r)
    {
        ans=min(ans,deep),Pre=pre,Next=next+cnt[u]-1;
        return ;
    }
    int c=s[l]-'a',tpre=pre,tnext=next;
    for(int i=0;i<c;i++)
        if(ch[u][i]!=-1)tpre+=cnt[ch[u][i]];
    if(id[u]!=-1)tpre++;
    for(int i=c+1;i<26;i++)
        if(ch[u][i]!=-1)tnext+=cnt[ch[u][i]];
    dfs(l+1,r,ch[u][c],tpre,tnext,deep+1);
    ans=min(ans,deep+Pre-pre),ans=min(ans,deep+1+Next-next);
}
void Solve(int l,int r)
{
    ans=INF,Pre=0,Next=0;
    dfs(l,r,0,0,0,1);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d",&n);
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",c);
            int len=strlen(c);
            insert(c,i);
            sum[i]=sum[i-1]+len;
            for(int j=sum[i-1];j<sum[i];j++)s[j]=c[j-sum[i-1]];
        }
        for(int i=1;i<=n;i++)
        {
            Solve(sum[i-1],sum[i]);
            printf("%d%c",ans,i==n?'\n':' ');
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值