洛谷 1456 隐藏口令 (链表/队列优化)

ps:洛谷题解里有O(n)的dp,这里就不再赘述了。

我们先来看一下这道题,我们的任务是从从一堆字符串中选出一个序列,使其字典序最小,很明显我们要找以最小的字母打头的序列,如果有多个我们再继续比对下一位。直到只剩下一个最小的字母,这就是我们的思路。

但是当我们遇到
aaaaaaaaaaaaaaaaaaaaaaaaaaab……
or
abcabcabcabcabcabcabcabcabc……

像这样的字符串就会卡成O(n^2)。

我们来观察一下这两个式子,发现他们的链表会有重叠部分。由于链表头总是最小的,所以当某链表的头被包含在前一个链表的尾部时,这个答案一定不如前一个链表优。所以这个链表我们会删去。值得一提的是,这个被删去的链表的下一位我们最好也访问一下,因为如果这个链表后面又是一个链表头,我们可以追加删去一条链表。

那我们来估一下时间复杂度,我们发现每条链表都没有重叠部分,那时间复杂度就是的链表平均长度 * 链表的条数,由于链表平均长度 * 链表条数 = 链表总长度。所以几乎是O(n)的。

实际上,很多人不是很会用链表,比如说我。我们可以观察到,我们使用的所有的链表实际上都只与链表尾部与链表头有关,那么我们只用一个只记录头与尾结构体队列就可以了,而对于删去一条链表,我们只需要阻止它重新入队就可以了。

附加代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct zt
{
    int f,v;
}dian[5000005];
queue<zt>q,p;
int n,ans,cnt,vis[5000005];
char num[5000005],b,c;
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        cin>>num[i];
        if(num[i] < b||!b) b = num[i];
    }
    for(int i = 0;i < n;i ++)
        if(num[i] == b) q.push((zt){i,i});
    while(q.size() > 1)
    {
        c = '0';cnt ++;
        while(!q.empty())
        {
            zt u = q.front();q.pop();
            if(u.v + 1 >= n) u.v -= n;
            if(c != '0'&&num[u.v + 1] > c) continue;
            vis[u.v + 1] = 1;
            if(vis[u.f]) continue;
            if(c == '0') c = num[u.v + 1];
            else if(c > num[u.v + 1]) {while(!p.empty()) p.pop();c = num[u.v + 1];}
            p.push((zt){u.f,u.v + 1});
        }
        while(!p.empty())
        {
            zt u = p.front();p.pop();q.push((zt){u.f,u.v});
        }
    }
    zt u = q.front();
    ans = u.v;
    ans = (n + ans - cnt) % n;
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值