1. 程式人生 > 實用技巧 >Mac使用觸控板或蘋果滑鼠時指標跳躍的解決方法

Mac使用觸控板或蘋果滑鼠時指標跳躍的解決方法

簡介

字尾陣列用於解決字串問題。至於到底解決什麼,並不是很清楚。

字尾排序

對於一個字串\(S\),我們可以\(O(n\log n)\)對它的所有後綴排序。

具體方法是倍增。我們先對\(S_{i,i+2^{k-1}-1}\)排序,然後再來解決\(S_{i,i+2^k-1}\)。這相當於我們對一些二元組\((rank_i,rank_{i+2^{k-1}})\)排序,最後得出答案。
對於某一維已經排好序的二元組,基數排序可以\(O(n)\)完美解決。

應用

光有這個玩意兒其實並沒有什麼用。我們真正需要的其實是由它衍生出來的\(height\)陣列求\(lcp\)

定義\(lcp(i,j)\)排名

\(i\)\(j\)的2個字尾的最長公共字首(的長度)。

下面給出一個重要性質:

\(lcp(i,j)=\min(lcp(i,k),lcp(k,j)),i\le k\le j\)

證明略。

那麼,我們設\(height_i=lcp(i,i-1)\),是不是就有\(lcp(i,j)=\min\{height_k\},i<k\le j\)?於是如果我們把\(height\)求出來,就可以用ST表算出任意2個字尾的\(lcp\)了。

現在假設\(h_i=height_{rk_i}\),那麼\(h_i\)就代表\(lcp(rk_i,rk_i-1)\)了。它有一個性質:

\(h_i\ge h_{i-1}-1\)

很抱歉,並沒有證明。

有了這個,就可以\(O(n)\)計算\(height_i\)了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
int n,m=127,sa[N+10],rk[N+10],tp[N+10],tx[N+10];
//sa[i]:排名為i的字尾的位置
//rk[i]:位置為i的字尾的排名 (可以得出rk[sa[i]]=sa[rk[i]]=i)
//tp[i]:排名為i的第二關鍵字的第一關鍵字的位置 (可以得出rk[tp[i]]就是第一關鍵字)
//tx[i]:一個桶,沒什麼好說的
char s[N+10];
inline int Read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	ch=='-'?f=-1:x=ch-'0';
	while(isdigit(ch=getchar())) x=x*10+ch-'0';
	return x*f;
}
inline void radix_sort()
{
	for(register int i=1;i<=m;++i) tx[i]=0;
	for(register int i=1;i<=n;++i) tx[rk[i]]++;
	for(register int i=2;i<=m;++i) tx[i]+=tx[i-1];//tx[i]字首和就可以第一關鍵字最多排到第幾名
	for(register int i=n;i>=1;--i) sa[tx[rk[tp[i]]]--]=tp[i];//倒序著做,這樣當第一關鍵字相同時,第二關鍵字大的會排在後面位置
}
inline void sufsort()
{
	for(register int i=1;i<=n;++i) rk[i]=s[i],tp[i]=i;
	radix_sort();
	for(register int k=1;k<=n;k<<=1)
	{
		int len=0;
		for(register int i=n-k+1;i<=n;++i) tp[++len]=i;
		for(register int i=1;i<=n;++i) 
			if(sa[i]>k) tp[++len]=sa[i]-k;
		radix_sort();
		swap(rk,tp);
		rk[sa[1]]=len=1;
		for(register int i=2;i<=n;++i) 
		{
			if(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k]) rk[sa[i]]=len;
			else rk[sa[i]]=++len;
		}
		m=len;
		if(len==n) break;
	}
}
inline void gethe()
{
	int k=0;
	for(register int i=1;i<=n;++i) rk[sa[i]]=i;
	for(register int i=1;i<=n;++i)
	{
		if(rk[i]==1) continue;
		if(k) --k;
		int j=sa[rk[i]-1];
		while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;
		he[rk[i]]=k;
	}
}
int main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	sufsort();
	for(register int i=1;i<=n;++i) cout<<sa[i]<<" ";
	return 0;
}