1. 程式人生 > 其它 >Codeforces Round #778 (Div. 1 + Div. 2)

Codeforces Round #778 (Div. 1 + Div. 2)

F. Minimal String Xoration

題目描述

點此看題

解法

\(f(s,d)\)\(t_i=s_{i\oplus d}\) 的字串 \(t\),可以將問題轉化成:把 \(f(s,0),f(s,1)...f(s,2^n-1)\) 按照字典序從小到大排序,那麼字典序最小的就是答案。

那麼可以考慮類似字尾陣列一樣倍增,假設現在我們知道在 \(2^k\) 的字首意義下,\(f(s,0\sim2^{n}-1)\) 的大小關係,我們考慮快速計算在 \(2^{k+1}\) 的字首意義下 \(f(s,0\sim 2^{n}-1)\) 的大小關係。

\(rk[i]\) 表示 \(f(s,i)\)

的字典序排名,那麼 \(f(s,i)\) 的特徵可以用 \((rk[i],rk[i\oplus 2^k])\) 來表示,我們把這個二元組排序,然後生成新一輪的 \(rk\) 即可(思路本質上就是字尾陣列的思路),時間複雜度 \(O(n^22^n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 1<<18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,b[26],rk[M],sa[M],nw[M];char s[M];
signed main()
{
	n=1<<read();scanf("%s",s);
	for(int i=0;i<n;i++) b[s[i]-'a']++;
	for(int i=1;i<26;i++) b[i]+=b[i-1];
	for(int i=0;i<n;i++) rk[i]=b[s[i]-'a'];
	for(int w=1;w<n;w<<=1)
	{
		for(int i=0;i<n;i++) sa[i]=i;
		sort(sa,sa+n,[&](int i,int j)
		{return rk[i]==rk[j]?rk[i^w]<rk[j^w]:rk[i]<rk[j];});
		nw[sa[0]]=1;int num=1;
		for(int i=1;i<n;i++)
			nw[sa[i]]=(rk[sa[i]]==rk[sa[i-1]]
			&& rk[sa[i]^w]==rk[sa[i-1]^w])?num:++num;
		memcpy(rk,nw,sizeof rk);
	}
	for(int i=0;i<n;i++)
		printf("%c",s[i^sa[0]]);
	puts("");
}