2020.01.04日常总结兼ST算法讲解

S T S T 算法讲解:ST表(ST算法)

我们来看这么一个问题:您有 n n 个数和 m m 次操作,每次操作求区间 [ l , r ] [l,r] 中的最大值。没有修改 1 n 2 × 1 0 5 , 1 m 1 0 6 1 \leq n \leq 2 \times 10^5,1 \leq m \leq 10^6

考虑最最简单的算法——暴力,你可以写出如下的代码:

for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
	register int l,r;
	scanf("%d%d",&l,&r);
	register int ans=a[l]-1;
	for(int j=l;j<=r;j++)
		ans=max(ans,a[j]);
	printf("%d\n",ans);
}

然而,它的时间复杂度是 O ( n × m ) O(n \times m) ,一定会超时 (除非数据巨水)

我们考虑用倍增算法优化它,记 F i , j F_{i,j} 表示 a [ i . . i + 2 j 1 ] a[i..i+2^j-1] 中的最大值,即从 a [ i ] a[i] 起连续的 2 j 2^j 个数中的最大值。

首先考虑如何把所有的 F i , j F_{i,j} 算出来,事实上,我们可以把它分成两部分,前半部分为 F i , j 1 F_{i,j-1} ,后半部分为 F i + 2 j 1 , j 1 F_{i+2^{j-1},j-1} ,所以我们有如下的递推式:

F i , j = m a x ( F i , j 1 , F i + 2 j 1 , j 1 ) F_{i,j}=max(F_{i,j-1},F_{i+2^{j-1},j-1})

这样做的时间复杂度为 O ( n × l o g n ) O(n \times log_n)

for(int j=1;j<=LogN;j++)
	for(int i=1;i+(1<<j)-1<=n;i++)
		f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);

然后考虑如何求最大值。因为取最大值时区间可以重叠,所以我们可以把区间分成重叠的两部分(记 s = l o g r l + 1 s=log_{r-l+1} ): F l , s , F r 2 s + 1 , s F_{l,s},F_{r-2^s+1,s} 。所以最大值为:

m a x ( F l , s , F r 2 s + 1 , s ) max(F_{l,s},F_{r-2^s+1,s})

return max(f[l][s],f[r-(1<<s)+1][s]);

【小技巧】: 因为现成的log2函数很慢,所以我们用 O ( n ) O(n) 的时间复杂度递推出所有数字的log值。

总的时间复杂度: O ( n × l o g n + m ) O(n \times log_n+m)


例题

洛谷P2412

【题意】: 蒟蒻HansBug在一本英语书里面找到了一个单词表,包含 N N 个单词(每个单词内包含大小写字母)。现在他想要找出某一段连续的单词内字典序最大的单词。该题目单词字典序比对过程中大小写不敏感,但是输出必须输出原单词

【思路】: ST算法的模板题,但是不是普通的整数ST算法,是处理字符串的算法。

【代码】:

#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
string MIN(string a,string b){
	string x=a,y=b;
	for(int i=0;i<x.size();i++)
		x[i]=tolower(x[i]);
	for(int i=0;i<y.size();i++)
		y[i]=tolower(y[i]);
	return x>y?a:b;
}
const int N=50100,logN=17;
string f[N][logN],a[N];
int n,m,Log[N];
string answer(int x,int y){
	register int s=Log[y-x+1];
	return MIN(f[x][s],f[y-(1<<s)+1][s]);
}
int main(){
	freopen("t1.in","r",stdin);
	n=read();m=read();Log[0]=-1;
	for(int i=1;i<=n;i++){
		cin>>a[i];f[i][0]=a[i];
	}
	for(int i=1;i<=n;i++)
		Log[i]=Log[i>>1]+1;
	for(int j=1;j<=logN;j++)
		for(int i=1;i+(1<<j)-1<=n;i++)
			f[i][j]=MIN(f[i][j-1],f[i+(1<<j-1)][j-1]);
	for(int i=1;i<=m;i++){
		register int x=read(),y=read();
		printf("%s\n",answer(x,y).c_str());
	}
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1767

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/103832373