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