1. 程式人生 > >bzoj 4104 解密運算 —— 思路

bzoj 4104 解密運算 —— 思路

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=4104

一開始發現了給出的順序是按這些末尾字元後面的字尾排序得到的;

然後發現可以一個一個把字串補全;

因為首先知道所有單個字元,排序後就是那些串的第一位,和末尾連起來,得到 n+1 個二元組;

然後把這 n+1 個二元組排序,再和末尾連起來,得到 n+1 個三元組;

以此類推,最後可以得到整個串,可惜這樣做是 n^2 的,空間也是;

考慮倍增?因為這樣得到的四元組的前兩個位置其實和當初得到的二元組一樣...

而且這個過程和字尾陣列的排序也很像啊!二元組是第二關鍵字,再加一個二元組是第一關鍵字...

然後想了一晚上也沒想明白怎麼倍增;

實際上,我又忘了字尾排序的另一種方法——(首字元相同的)去掉首字元,看後面字尾的排序;

這道題——給出的末尾字元是首字元的話,那麼它們已經按去掉首字元的字尾後排好序了;

所以把每行的字元以字元大小為第一關鍵字,行數為第二關鍵字排序,就知道在原串中這些首字元對應的字尾的排名了;

而這些行也是按字典序排序的;

所以知道了每一行末尾字元是哪一行的開頭;

於是從 “.” 開始,按排名對應回每一行,就倒著得到了原串!

在我原來的想法中,問題在於相同的字元作為開頭,無法知道誰對應哪個末尾;

而如果利用行的排名,相同字元之間的排名也明確了,就不需要每次再排序來確定對應的末尾字元;

字尾排序的那個方法(去掉首字元)...真常用啊...

程式碼如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int const xn=2e5+5;
int n,m,rk[xn],s[xn],ans[xn];
struct N{int s,id;}p[xn];
bool cmp(N x,N y){return x.s<y.s||(x.s==y.s&&x.id<y.id);}
bool cmp2(N x,N y){return x.id<y.id;} int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n+1;i++) scanf("%d",&s[i]),p[i].s=s[i],p[i].id=i; sort(p+1,p+n+2,cmp); for(int i=1;i<=n+1;i++)rk[p[i].id]=i; int top=0,nw=p[1].id; while(top<n+1)ans[++top]=s[nw],nw=rk[nw]; while(top>1)printf("%d ",ans[top--]); puts(""); return 0; }