CF1654B 做题历程

每次删前缀,容易想到从后往前做,随便手模了一下,写出了如下代码:

int T=read();
    while(T--){
        scanf("%s",s+1);
        int n=strlen(s+1);
        memset(vis,0,sizeof(vis));
        for(int i=n;i;--i)
            if(vis[s[i]-'a']){
                for(int j=i+1;j<=n;++j)
                    putchar(s[j]);
                puts("");
                break;
            }else vis[s[i]-'a']=1;
    }

哈哈,发现过不了样例最后一个 case,发现可能前面就会被卡住,不一定是要最后一个后缀中只出现过一次的点

哈哈,然后没有细想怎么解决这个问题,直接放弃了从后往前做的思路,看到 2e5 给了 2s,开始考虑根号分治之类的

发现如果串长小的话可以直接 Z 函数 \(O(n^2)\) 乱糊,可是串长大了还是不会

考虑 nlogn 算法,考虑能不能直接二分出最后停住的位置

思考了一下,发现好像没有单调性,G

然后考虑如何优化这个暴力匹配的过程

发现每一次其实在求固定一个后缀,找从它后面开始的后缀中与之最大的 lcp,直接上一个 SA,考虑我 height 数组是在干什么

假如我不管“从后面开始”这个限制,我直接 \(\max\{height[rk[i]],height[rk[i]+1]\}\) 即可

假设现在我已经删到了位置 \(now\),我把 \(height[rk[1\dots now-1]]=1e9\),那么我在 \(height\) 数组上直接从 \(rk[i]+1\) 往右扫,扫到第一个不是 \(1e9\) 的位置就好了

同理可以从 \(rk[i]\) 往左扫,取第一个是因为我 lcp 显然是单调递减的

这样直接干一共是 \(O(n^2)\) 的,我考虑线段树优化一下这个找第一个不是 \(1e9\) 的位置的过程

我直接线段树维护一下 height 最小值,二分一下即可。

每个点的 height 只会被标为 \(1e9\) 一次,所以复杂度是对的 \(O(n\log n)\)

然后写出了如下代码:

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N = 2e5+5;

// char buf[1<<23],*p1=buf,*p2=buf;
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;
}

struct Tree{int cv,mn;}t[N<<2];
int rk[N],sa[N],tax[N],tmp[N],height[N];
char s[N];
int n,m,T;

void radix_sort(){
    for(int i=1;i<=m;i++)tax[i]=0;
    for(int i=1;i<=n;i++)tax[rk[i]]++;
    for(int i=1;i<=m;i++)tax[i]+=tax[i-1];
    for(int i=n;i>=1;i--)sa[tax[rk[tmp[i]]]--]=tmp[i];
}
void suffix_sort(){
    m=200;
    for(int i=1;i<=n;i++)
        rk[i]=s[i]-'a'+1,tmp[i]=i;
    radix_sort();
    for(int w=1,p=0;p<n;m=p,w<<=1){
        p=0;
        for(int i=1;i<=w;i++)tmp[++p]=n-w+i;
        for(int i=1;i<=n;i++)if(sa[i]>w)tmp[++p]=sa[i]-w;
        radix_sort(),swap(tmp,rk);
        rk[sa[1]]=p=1;
        for(int i=2;i<=n;i++)
            rk[sa[i]]=(tmp[sa[i-1]]==tmp[sa[i]]&&tmp[sa[i-1]+w]==tmp[sa[i]+w])?p:++p;       
    }
}
void get_height() {
    int k=0;
    for(int i=1;i<=n;i++) {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rk[i]]=k;
    }
}

void pushup(int rt){t[rt].mn=min(t[ls].mn,t[rs].mn);}
void pushdown(int rt){if(t[rt].cv)t[ls].cv=t[rs].cv=1,t[ls].mn=t[rs].mn=1e9,t[rt].cv=0;}
void build(int l,int r,int rt){
    if(l==r)return t[rt].mn=height[l],void(); pushdown(rt);
    build(l,mid,ls),build(mid+1,r,rs),pushup(rt);
}
void alter(int l,int r,int rt,int id){
    if(l==r)return t[rt].mn=1e9,void(); pushdown(rt);
    id<=mid?alter(l,mid,ls,id):alter(mid+1,r,rs,id);
    pushup(rt);
}
int query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R)return t[rt].mn; pushdown(rt);
    if(R<=mid)return query(l,mid,ls,L,R);
    if(mid<L)return query(mid+1,r,rs,L,R);
    return min(query(l,mid,ls,L,R),query(mid+1,r,rs,L,R));
}

signed main(){
    T=read();
    while(T--){
        scanf("%s",s+1),n=strlen(s+1);
        suffix_sort(),get_height(),build(1,n,1);
        int now=1,v;
        while(1){
            int l=rk[now]+1,r=n,len=0;
            // cout<<"now:"<<now<<" "<<rk[now]<<endl;
            // for(int i=1;i<=n;++i){
            //     int t=query(1,n,1,i,rk[i]);
            //     if(t==1e9)cout<<-1<<" ";
            //     else cout<<t<<" ";
            // }puts("");
            while(l<=r){
                int midd=(l+r)>>1,tp=query(1,n,1,rk[now]+1,midd);
                if(tp!=1e9)len=max(len,tp),r=midd-1;
                else l=midd+1;
            }
            l=1,r=rk[now];
            while(l<=r){
                int midd=(l+r)>>1,tp=query(1,n,1,midd,rk[now]);
                if(tp!=1e9)len=max(len,tp),l=midd+1;
                else r=midd-1; 
            }

            if(len==0)break;

            // cout<<len<<endl;
            for(int i=now;i<=now+len-1;++i)
                alter(1,n,1,rk[i]);
            now+=len;
        }
        for(int i=now;i<=n;++i)putchar(s[i]);
        puts("");
        t[1].cv=1;
    }
    return 0;
}

然后一通调试,就是不知道怎么错了,一共搞了 2h+

心态有点炸了,看了一眼榜,哈哈,人均会 ABCD

开始看后面的题,时间太仓促,每道题都只是扫完题面,发现第一眼没有好想法,走人

滚回来想 T2,看到榜上全是 4 分钟切掉的,意识到这道题应该是一道签到题,我搞复杂了

开始按 Ctrl+Z,又重拾最开始的思路,开始倒着想

想了 2min,哈哈,会了,花了 1min 敲代码,哈哈,过了

    T=read();
    while(T--){
        memset(vis,0,sizeof(vis));
        scanf("%s",s+1);
        n=strlen(s+1),p=n;
        for(int i=n;i;--i){
            if(!vis[s[i]-'a'])p=i;
            vis[s[i]-'a']=1;
        }
        for(int i=p;i<=n;++i)
            putchar(s[i]);
        puts("");
    }

这时候已经无心思考 C 了,摆着 T3 的题面,大脑乱掉了,GG

所以反思一下失误在哪里,为啥会花这么多时间写了一坨 rubbish

然后没有细想怎么解决这个问题,直接放弃了从后往前做的思路

主观地来说,是这个 sb 凭感觉做题,毛毛躁躁,抓住一半就跑

客观上,明明一道 \(O(n)\) 的题,不是很懂为啥给了 2e5 2s

哈哈,掉大分,哈哈

posted @ 2022-03-20 23:24  _Famiglistimo  阅读(35)  评论(0编辑  收藏  举报