zoukankan      html  css  js  c++  java
  • 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
    ",ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    SQL UNION 和 UNION ALL 操作符
    JavaScript--验证码随机生成
    C#基础 out和ref
    远程连接身份验证错误,又找不到加密Oracle修正
    ASP.Net WebAPI -- 简单实现增删改查
    SpringBoot发送邮箱验证码
    VMware虚拟机安装Linux系统详细教程
    SpringBoot实现登陆拦截
    JavaScript 实例、构造函数、原型对象关系图
    JavaScript创建对象的几种方式总结
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF802I.html
Copyright © 2011-2022 走看看