Codeforces 873F Forbidden Indices 字符串 SAM/(SA+单调栈)

原文链接https://www.cnblogs.com/zhouzhendong/p/9256033.html

题目传送门 - CF873F

题意

  给定长度为 $n$ 的字符串 $s$,以及给定这个字符串每一个位置是否 “禁止结尾” 的信息。

  一个字符串 $a$ 的价值为 $|a|\times f(a)$ 。

  其中 $f(a)$为 $a$ 在 $s$ 中的匹配次数(如果匹配的结尾为禁止结尾点,那么不算匹配成功)

  问在所有的字符串 $a$ 中,$\max(|a|\times f(a)$ 的值。

  $n\leq 2\times 10 ^5$

题解

  大概是好久没做 SAM 了,第一个想到的就是 SA 。其实用 SAM 做非常简单,不知道高到哪里去了……

  这里讲 SA 做法。

  首先考虑到 SA 对后缀开头点掌握的比较好,所以我们将原串翻转,把问题转化成开头禁止。

  然后对 $s$ 串跑一下 SA 。

  然后假装那些开头禁止的串不存在,就转化成一个经典的问题,直接单调栈搞定。

  至于单调栈原理,这里有一道类似的运用单调栈的例题。

  https://www.cnblogs.com/zhouzhendong/p/9026184.html

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005;
int n;
int SA[N],rank[N],tmp[N],height[N],tax[N];
int sum[N];
int st[N],top,pos[N];
char s[N],t[N];
void Sort(int n,int m){
    for (int i=0;i<=m;i++)
        tax[i]=0;
    for (int i=1;i<=n;i++)
        tax[rank[i]]++;
    for (int i=1;i<=m;i++)
        tax[i]+=tax[i-1];
    for (int i=n;i>=1;i--)
        SA[tax[rank[tmp[i]]]--]=tmp[i];
}
bool cmp(int rk[],int x,int y,int w){
    return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
}
void Suffix_Array(char s[],int n){
    memset(SA,0,sizeof SA);
    memset(tmp,0,sizeof tmp);
    memset(rank,0,sizeof rank);
    memset(height,0,sizeof height);
    for (int i=1;i<=n;i++)
        rank[i]=s[i],tmp[i]=i;
    int m=234;
    Sort(n,m);
    for (int w=1,p=0;p<n;w<<=1,m=p){
        p=0;
        for (int i=n-w+1;i<=n;i++)
            tmp[++p]=i;
        for (int i=1;i<=n;i++)
            if (SA[i]>w)
                tmp[++p]=SA[i]-w;
        Sort(n,m);
        swap(rank,tmp);
        rank[SA[1]]=p=1;
        for (int i=2;i<=n;i++)
            rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
    }
    for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
        for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
    height[1]=0;
}
int main(){
	scanf("%d",&n);
	scanf("%s%s",s+1,t+1);
	for (int i=1;i<=n/2;i++){
		swap(s[i],s[n-i+1]);
		swap(t[i],t[n-i+1]);
	}
	Suffix_Array(s,n);
	for (int i=1;i<=n;i++)
		if (t[SA[i]]==‘1‘)
			sum[i]=sum[i-1];
		else
			sum[i]=sum[i-1]+1;
	LL ans=0;
	for (int i=1;i<=n;i++)
		if (t[i]==‘0‘)
			ans=max(ans,(LL)n-i+1);
	top=1,st[top]=1,pos[top]=1;
	height[n+1]=0;
	for (int i=2;i<=n+1;i++){
		int j=st[top],p=i;
		while (height[j]>height[i]){
			ans=max(ans,1LL*height[j]*(sum[i-1]-sum[pos[top]-2]));
			p=pos[top];
			j=st[--top];
		}
		st[++top]=i,pos[top]=p;
	}
	printf("%I64d",ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/9256033.html

时间: 2024-08-27 21:01:04

Codeforces 873F Forbidden Indices 字符串 SAM/(SA+单调栈)的相关文章

CodeForces 873F Forbidden Indices 后缀数组

忘了当时怎么做的了,先把代码贴上,保存一下后缀数组模板. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define REP(i, a, b) for(int i = a; i < b; i++) #define PER(i, a, b) for(int i = b - 1; i >= a; i--) typedef long long LL;

Codeforces 802I Fake News (hard) (SA+单调栈) 或 SAM

原文链接http://www.cnblogs.com/zhouzhendong/p/9026184.html 题目传送门 - Codeforces 802I 题意 求一个串中,所有本质不同子串的出现次数的平方和. $|s|\leq 10^5$ 题解 首先,这一题用$SAM$做就是模板题,比较简单. 但是,本着练一练$SA$的心态,我开始了$SA+单调栈$的苦海. 真毒瘤. 这里讲一讲$SA$的做法,也是经典的做法. $SA$闭着眼睛先写了再说. 首先,我们考虑出现次数大于$1$次的子串. 考虑按

luogu2178/bzoj4199 品酒大会 (SA+单调栈)

他要求的就是lcp(x,y)>=i的(x,y)的个数和a[x]*a[y]的最大值 做一下后缀和,就只要求lcp=i的了 既然lcp(x,y)=min(h[rank[x]+1],..,[h[rank[y]]]) 那么我们求出来对于每一个h,以它作为最小值的区间的左右端点就可以了,这个可以用单调栈,具体做法见Neat Tree(?哪里具体了) 假设L是i左面第一个h小于等于它的,R是i右面第一个小于它的(一定要一边有=一边没有,很关键) 那就相当于lcp(x,y)=h[i] ,rank[x]∈[L,

Codeforces 452E Three strings 字符串 SAM

原文链接https://www.cnblogs.com/zhouzhendong/p/CF542E.html 题目传送门 - CF452E 题意 给定三个字符串 $s1,s2,s3$ ,对于所有 $L\in{1,2,\cdots,min(|s1|,|s2|,|s3|)}$ ,输出 $f(L)$ . 其中 $f(L)$ 表示满足 $s_k[i_k,\cdots,i_k+L-1]$ 全部相同的 $i_1,i_2,i_3$ 的个数. 答案对 $10^9+7$ 取模. $|s1|+|s2|+|s3|\l

Codeforces 319B. Psychos in a Line【单调栈】

题目链接: http://codeforces.com/problemset/problem/319/B 题意: 一串数列,每一个值如果大于相邻右一位的值的话,那么就可以把右边这个值"吃掉"(右一位消失,原来的值不变),问需要吃多少次才能到达无法再吃的状态. 思路: 利用栈.遍历一遍数组,处理每个值的时候,如果栈顶的元素小于该值,那么将其弹出,知道栈顶元素大于该值或者栈为空,栈内的每个元素记录下一个属性:他是在第几次被"吃掉",进栈的新元素的被吃次数就是它弹出去的元

【POJ3415】 Common Substrings (SA+单调栈)

这道是求长度不小于 k 的公共子串的个数...很不幸,我又TLE了... 解法参考论文以及下面的链接 http://www.cnblogs.com/vongang/archive/2012/11/20/2778481.html http://hi.baidu.com/fpkelejggfbfimd/item/5c76cfcba28fba26e90f2ea6 1 const maxn=200419; 2 var 3 c,h,rank,sa,x,y,stack:array[0..maxn] of l

Codeforces Round #622 (Div. 2)C(单调栈,DP)

构造出的结果一定是一个单峰/\这种样子的 1 #define HAVE_STRUCT_TIMESPEC 2 #include<bits/stdc++.h> 3 using namespace std; 4 long long a[500007]; 5 pair<long long,long long>stk[500007]; 6 long long l[500007],r[500007];//记录左/右边最近的比当前小的位置 7 long long ans[500007]; 8 l

Educational Codeforces Round 23 D. Imbalanced Array(单调栈)

题目链接:Educational Codeforces Round 23 D. Imbalanced Array 题意: 给你n个数,定义一个区间的不平衡因子为该区间最大值-最小值. 然后问你这n个数所有的区间的不平衡因子和 题解: 对每一个数算贡献,a[i]的贡献为 当a[i]为最大值时的 a[i]*(i-l+1)*(r-i+1) - 当a[i]为最小值时的a[i]*(i-l+1)*(r-i+1). 计算a[i]的l和r时,用单调栈维护.具体看代码,模拟一下就知道了. 然后把所有的贡献加起来.

CodeForces 548D 单调栈

Mike and Feet Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 548D Appoint description: Description Mike is the president of country What-The-Fatherland. There are n bears living in this