• [HihoCoder1413]Rikka with String


    vjudge

    题意

    给你一个串,问你把每个位置的字符替换成#后串中有多少本质不同的子串。
    (nle 3*10^5)

    sol

    首先可以计算出原串里面有多少本质不同的子串。显然就是(sum_{i=1}^{tot}len_i-len_{fa_i})

    然后考虑把(i)位置替换成了#,那么你从(1..i)中任选左端点,(i..n)中任选右端点,这样的子串一定会是本质不同的。(一定含有字符#,满足长度不同或#的出现位置不同)

    所以对于位置(i),答案先加上(i*(n-i+1))

    然后也可以再给答案加上原有的本质不同的子串数目,这样我们就只需要求出:把(i)位置改成#后有多少本质不同的子串不再出现了(被砍断)。

    考虑一个(SAM)中的状态(i),设其最大长度(len_i)(right)集合中的最小最大元素分别为(l_i,r_i)

    对于区间([r_i-len_i+1,min(r_i-len_{fa_i},l_i)])(如果合法的话),我们会给这个区间加上一个首项是(1)公差也是(1)的等差数列。

    因为这个状态(i)本身是表示了(len_i-len_{fa_i}+1)个连续长度的子串,所以在这个区间内每向右移动一下就会有一个子串被砍断,因此是一个公差为(1)的等差数列。

    对于区间([min(r_i-len_{fa_i},l_i)+1,l_i])(如果合法的话),一旦#出现在这个区间里那么(i)这个状态表示的所有长度大于等于一定值的子串就都会被砍断。所以给这个区间集体加上前一个区间的长度,即(min(r_i-len_{fa_i},l_i)-(r_i-len_i+1)+1)

    等差数列用二阶差分维护一下就可以了,注意原一阶差分(第二种情况就是一个一阶差分)对应的二阶差分。所以构出(SAM)后的复杂度可以做到线性。

    code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N = 6e5+5;
    int n,last=1,tot=1,tr[N][26],fa[N],len[N],l[N],r[N],t[N],a[N];
    char s[N];long long sum,ans[N];
    void extend(int c)
    {
    	int v=last,u=++tot;last=u;
    	len[u]=len[v]+1;
    	while (v&&!tr[v][c]) tr[v][c]=u,v=fa[v];
    	if (!v) fa[u]=1;
    	else{
    		int x=tr[v][c];
    		if (len[x]==len[v]+1) fa[u]=x;
    		else{
    			int y=++tot;
    			memcpy(tr[y],tr[x],sizeof(tr[y]));
    			fa[y]=fa[x];fa[x]=fa[u]=y;len[y]=len[v]+1;
    			while (v&&tr[v][c]==x) tr[v][c]=y,v=fa[v];
    		}
    	}
    }
    int main()
    {
    	scanf("%d",&n);scanf("%s",s+1);
    	memset(l,63,sizeof(l));
    	for (int i=1;i<=n;++i) extend(s[i]-'a'),l[last]=r[last]=i;
    	for (int i=1;i<=tot;++i) ++t[len[i]];
    	for (int i=1;i<=tot;++i) t[i]+=t[i-1];
    	for (int i=1;i<=tot;++i) a[t[len[i]]--]=i;
    	for (int i=tot;i;--i)
    	{
    		l[fa[a[i]]]=min(l[fa[a[i]]],l[a[i]]);
    		r[fa[a[i]]]=max(r[fa[a[i]]],r[a[i]]);
    		sum+=len[i]-len[fa[i]];
    	}
    	for (int i=2;i<=tot;++i)
    	{
    		int L=r[i]-len[i]+1,R=min(r[i]-len[fa[i]],l[i]),Len=R-L+1;
    		if (L<=R) ans[L]+=1,ans[R+1]-=Len+1,ans[R+2]+=Len;
    		L=R+1;R=l[i];
    		if (L<=R) ans[L]+=Len,ans[L+1]-=Len,ans[R+1]-=Len,ans[R+2]+=Len;
    	}
    	for (int i=1;i<=n;++i) ans[i]+=ans[i-1];
    	for (int i=1;i<=n;++i) ans[i]+=ans[i-1];
    	for (int i=1;i<=n;++i) printf("%lld ",1ll*i*(n-i+1)+sum-ans[i]);
    	puts("");return 0;
    }
    
  • 相关阅读:
    数据结构----------堆栈
    数据结构----------双向链表
    剑指offer32----之字形打印一颗二叉树
    剑指offer31----栈的压入、弹出序列
    MySQL----日期处理函数
    1.1 文档PUT内部原理
    1 集群状态、增删改查、全量替换、强制创建、设置单个index的分片数副本数
    编译问题:'<invalid-global-code>' does not contain a definition for 'Store' and no extension method 'XXX' accepting a first argument of type '<invalid-global-code>' could be found
    ES timeout 的一些笔记
    elasticsearch 5.0以上不支持consistency 和 quorum
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8799177.html
Copyright © 2020-2023  润新知