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;
}