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$次的子串。

  考虑按照$SA$数组的顺序来进行处理,这样得到的后缀的字典序不断变大。

  如果要统计一个串与前一个串的$LCP$出现了多少次,该如何统计?

  显然是往前和往后都找到第一个$LCP$比当前小的停止并统计。

  于是我们用单调栈来维护一个$height$升序的序列。具体的统计方法这里不多赘述,可以直接查阅代码。比较明了。

  单调栈要注意处理当前$LCP$和栈顶$LCP$长度值相同的情况。

  考虑只出现一次的串个数。对于第$i$大的后缀(即$SA[i]$),之前统计到的是$SA[i]$与$SA[i-1]$、$SA[i]$与$SA[i+1]$的$LCP$的$\max$。于是没用统计到的就是剩下的。于是当前后缀出现依次的串对答案的贡献就是$len-\max$。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
int T,n,m,SA[N],rank[N],height[N],tmp[N],tax[N];
char s[N];
int st[N],top,pos[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;
	m=127;
	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++);
}
int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%s",s+1);
		n=strlen(s+1);
		Suffix_Array(s,n);
		LL ans=0;
		top=0;
		memset(st,0,sizeof st);
		memset(pos,0,sizeof pos);
		SA[n+1]=height[0]=0;
		for (int i=2;i<=n+1;i++){
			int nowpos=i,len=height[i];
			while (top>0&&st[top]>len){
				LL v1=st[top]-max(st[top-1],len);
				LL v2=i-pos[top]+1;
				ans+=v1*v2*v2;
				nowpos=pos[top--];
			}
			while (top>0&&st[top]==len)
				nowpos=pos[top--];
			st[++top]=len;
			pos[top]=nowpos;
		}
		for (int i=1;i<=n;i++)
			ans+=n-i+1-max(height[rank[i]],height[rank[i]+1]);
		printf("%I64d\n",ans);
	}
	return 0;
}

  

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

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

Codeforces 802I Fake News (hard) (SA+单调栈) 或 SAM的相关文章

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)$

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时,用单调栈维护.具体看代码,模拟一下就知道了. 然后把所有的贡献加起来.

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,

【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 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

「AHOI 2013」差异 「SA」「单调栈」

首先明确题目要我们求什么.看到后面的 \(LCP(T_i, T_j)\) 很容易用后缀数组将其转化成 \(\min_{rk[i] < k \leq rk[j]}{height[k]}\).\((若rk[i] < rk[j])\) 考虑计算每个位置的h作为min出现的次数.很明显这个东西可以用单调栈一步求出来.那么就转为计算 \(\sum_{p = l} ^ {i} \sum_{p = i} ^ {r} (n - sa[i - 1] + 1) + (n - sa[i] + 1)\). 然后大家只

[Codeforces Round #622 (Div. 2)] - C2. Skyscrapers (hard version) (单调栈)

[Codeforces Round #622 (Div. 2)] - C2. Skyscrapers (hard version) (单调栈) C2. Skyscrapers (hard version) time limit per test 3 seconds memory limit per test 512 megabytes input standard input output standard output This is a harder version of the probl

Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version) 单调栈

Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version) 问题 传送门 我是参考了这篇题解传送门,然后按着思路做出了的(但大佬题解中的sumr[]数组操作我没看懂,然后自己改了改). 摘抄: 维护峰值最优 找左右边的第一个比自己小的元素,维护前缀和,找最大的峰值 l[i]:用单调栈维护左边第一个比它小的数 r[i]:用单调栈维护右边第一个比它小的数 suml[i]:左边的前缀和 sumr[i]:右边的前缀和 然后遍历一遍数组,找到

Codeforces Round #305 (Div. 2)D---Mike and Feet(单调栈)

Mike is the president of country What-The-Fatherland. There are n bears living in this country besides Mike. All of them are standing in a line and they are numbered from 1 to n from left to right. i-th bear is exactly ai feet high. A group of bears