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

 

posted @ 2018-12-07 09:02  Zinn  阅读(127)  评论(0编辑  收藏  举报