【后缀数组/细节题】 CC_EST Equivalent Suffix Tries

【题目】
原题地址
题目大意:给出一个字符串,求与这个字符串【后缀树同构】的字符串有多少个。

【题目分析】
寻找后缀树的性质!

【解题思路】
这道题是真的真的很难写。
首先明确一下,后缀树是由字符串每个后缀建出的Trie。设每个后缀 s u f i suf_i sufi s [ i . . n ] s[i..n] s[i..n]

观察后缀树的特点,我们可以发现,字符串中出现了多少个字符,根节点就有多少个分叉,也就是说最后一定是乘上一个组合数的形式。

接着观察后缀树的叶子节点,可以发现,深度一定有 n , n − 1 , n − 2 n,n-1,n-2 n,n1,n2…直到 n − i n-i ni深度的叶子没有出现。这说明 s u f n − i + 1 suf_{n-i+1} sufni+1一定覆盖在了 s u f 1 suf_1 suf1 s u f n − i suf_{n-i} sufni中的某一个上。同时我们可以显然看出,如果我们确定 s u f n − i + 1 suf_{n-i+1} sufni+1覆盖在哪个后缀上,整颗后缀树的形态一定是确定的,因此我们只用考虑最长的没有出现在后缀树中的后缀。想找到它的位置,我们可以直接用后缀数组,找到第一个 h e i g h t = = l e n height==len height==len的后缀。

进一步分析,我们可以得到,若这个后缀可以覆盖在 k k k个后缀上,设字符集为 p p p那么最后的答案就是 C 26 p ∗ k C^p_{26}*k C26pk

接下来问题就变成了:如果我枚举覆盖在哪个后缀 i i i上,如何 n o w now now的覆盖判断是否合法?

解决这个问题还要考虑后缀树同构的必要条件:我们发现后缀树上会有一些分叉,也就是说,在这些分叉的地方必须要有不同的字符,进而我们可以通过原字符串的 h e i g h t height height,来限制每个位置的可选字符(就是哪些位置的字符不能一样)。

有了这些限制,我们对于每个后缀 i i i,看它和now的 L C P LCP LCP,若 L C P &lt; l e n n o w LCP&lt;len_{now} LCP<lennow,那么显然不能覆盖。
否则的话我们暴力构出整个串(实际上不用构出),判断路径上的所有限制位置是否合法即可。

这里的复杂度证明我有点说不清楚,大概因为每个限制位置只会判断很少次,所以是合理的(至少我好像不会构造数据)。
(同学说是调和级数级别的,mengbi)

总的复杂度是后缀数组的 O ( n l o g n ) O(nlogn) O(nlogn),以及十分大的常数。

tip:这道题还可以用哈希解决。
大佬们如果看懂了题解欢迎指正qwq
官方题解

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=2e5+10;
const int mod=42424242;
int n,T;
LL ans,bas;
int r[N],wa[N],wb[N],wx[N],wy[N];
int sa[N],rk[N],vis[N],height[N];
char st[N];

void init()
{
	scanf("%s",st);n=strlen(st);
	for(int i=0;i<=n+100;++i) r[i]=wa[i]=wb[i]=wx[i]=wy[i]=sa[i]=rk[i]=height[i]=0;
	for(int i=0;i<=26;++i) vis[i]=0;
	for(int i=0;i<n;++i) r[i]=st[i]-'a'+1;
	//printf("!!!");for(int i=0;i<n;++i) printf("%d ",r[i]);puts("");
}

bool cmp(int *r,int a,int b,int l) {return r[a]==r[b] && r[a+l]==r[b+l];}

void da(int *r,int n,int m)
{
	int *x=wa,*y=wb,*t,i,j,p;
	for(i=0;i<m;++i) wx[i]=0;
	for(i=0;i<n;++i) wx[x[i]=r[i]]++;
	for(i=1;i<m;++i) wx[i]+=wx[i-1];
	for(i=n-1;~i;--i) sa[--wx[x[i]]]=i;
	for(j=1,p=0;p<n;j<<=1,m=p)
	{
		for(p=0,i=n-j;i<n;++i) y[p++]=i;
		for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;++i) wy[i]=x[y[i]];
		for(i=0;i<m;++i) wx[i]=0;
		for(i=0;i<n;++i) wx[wy[i]]++;
		for(i=1;i<m;++i) wx[i]+=wx[i-1];
		for(i=n-1;~i;--i) sa[--wx[wy[i]]]=y[i];
		for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i) 
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}

void call_height(int *r,int n)
{
	int i,j,k=0;
	for(i=1;i<=n;++i) rk[sa[i]]=i;
	for(i=0;i<n;height[rk[i++]]=k)
		for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
}

void solve()
{
	if(n==1) {puts("26");return;}
	for(int i=0;i<n;++i) vis[r[i]]=1;
	ans=1;bas=26;
	for(int i=1;i<=26;++i) 
		if(vis[i]) (ans*=bas)%=mod,--bas;
	da(r,n+1,256);
	call_height(r,n);
	//puts("!!");for(int i=1;i<=n;++i) printf("%d\n",height[i]);puts("!!!");
	int brk=n+1;
	for(int i=1;i<=n;++i)
		if(max(height[i],height[i+1])==n-sa[i])
			brk=min(brk,sa[i]);
	if(brk==1) {puts("26");return;}
	if(brk==n+1) {printf("%lld\n",ans);return;}
	
	int cnt=rk[brk],len=n-brk,res=1;
	int L=cnt,R=cnt,surep=brk-1;
	for(int i=0;i<brk;++i) 
	{
		int tmp=n+1,now=rk[i]-1;
		while(now && sa[now]>=brk) tmp=min(tmp,height[++now]);
		if(now) surep=max(surep,i+min(tmp,height[now+1])-1);
		tmp=n+1;now=rk[i]+1;
		while(now<=n && sa[now]>=brk) tmp=min(tmp,height[now++]);
		if(now<=n) surep=max(surep,i+min(tmp,height[now])-1);
	}
	int surel=surep-brk+1;
	
	int now=0,val=n+1;
	for(int i=1;i<=n;++i)
	{
		val=min(val,height[i]);
		if(sa[i]<=brk)
		{
			sa[++now]=sa[i];
			if(i==cnt) cnt=now;
			height[now]=val;val=n+1;
		}
	}
	
	n=now;height[n+1]=0;
	for(int i=1;i<=n;++i) rk[sa[i]]=i;
	
	for(int i=cnt;i;--i)
	{
		if(height[i]<surel+1) {L=i-1;break;}
		if(height[i]<len) ++res;
	}
	for(int i=cnt+1;i<=n+1;++i)
	{
		if(height[i]<surel+1) {R=i;break;}
		if(height[i]<len) ++res;	
	} 
	while(L)
	{
		if(height[L+1]<surel) break;
		int nexl=L-1;
		for(int i=L;i;--i)
			if(height[i]<surel+1) {nexl=i-1;break;}
		bool flag=1;
		for(int i=L;i>nexl;--i)
			if(sa[i] && r[sa[i]-1]==r[brk-1]) {flag=0;break;}
		if(!flag) {L=nexl;continue;}
		++res;
		for(int i=L;i>nexl+1;--i)
			if(height[i]<len) ++res;
		L=nexl;
	}
	while(R<=n)
	{
		if(height[R]<surel) break;
		int nexr=R+1;
		for(int i=R;i<=n;++i)
			if(height[i+1]<surel+1) {nexr=i+1;break;} 
		bool flag=1;
		for(int i=R;i<nexr;++i) 
			if(sa[i] && r[sa[i]-1]==r[brk-1]) {flag=0;break;}
		if(!flag) {R=nexr;continue;}
		++res;
		for(int i=R;i<nexr-1;++i)
			if(height[i+1]<len) ++res;
		R=nexr;
	}
	printf("%lld\n",(LL)ans*res%mod);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CC_EST.in","r",stdin);
	freopen("CC_EST.out","w",stdout);
#endif
	scanf("%d",&T);
	while(T--)
	{
		init();
		solve();
	}

	return 0;
}

【总结】
观察性质,一顿乱搞,得到答案。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
create_power_domain PD_TOP create_power_domain PD1 -elements {Block1} -scope Block1 create_power_domain PD2 -elements {Block2} -scope Block2 create_power_domain PD3 -elements {Block3} -scope Block3 create_power_domain PD_TOP create_power_domain PD1 -elements {Block1} -scope Block1 create_power_domain PD2 -elements {Block2 Block3} create_power_domain PD_TOP set_scope Block1 create_power_domain PD1 set_scope ... create_power_domain PD2 -elements {Block2 Block3} create_power_domain domain_name [-elements list] [-supply {supply_set_handle supply_set_name}] [-include_scope] create_supply_port port_name create_supply_net net_name [-domain domain_name] [-reuse] [-resolve unresolved | parallel | one_hot | parallel_one_hot | user_defined_resolution_function] connect_supply_net supply_net_name -ports list create_supply_port VDD1P -domain Block1/PD1 create_supply_net VDD1 -domain Block1/PD1 connect_supply_net Block1/VDD1 -ports Block1/VDD1P connect_supply_net VDD1 -ports Block1/VDD1P set_domain_supply_net domain_name -primary_power_net supply_net_name -primary_ground_net supply_net_name set_domain_supply_net Block1/PD1 -primary_power_net Block1/VDD1 -primary_ground_net Block1/GND create_power_switch switch_name -domain domain_name -output_supply_port {port_name supply_net_name} {-input_supply_port {port_name supply_net_name}}* {-control_port {port_name net_name}}* {-on_state {state_name input_supply_port {boolean_function}}}* [-ack_port {port_name net_name [{boolean_function}]}]* [-ack_delay {port_name delay}]* [-off_state {state_name {boolean_function}}]* [-on_partial_state {state_name {boolean_function}}]* [-error_state {state_name {boolean_function}}]* map_power_switch switch_name -domain domain_name -lib_cells list create_supply_set supply_set_name associate_supply_set supply_set_name -handle supply_set_name set_equivalent -nets supply_nets | -sets supply_sets prompt> set_equivalent -nets {VDDa VDDb VDDc} # equivalent supply nets prompt> set_equivalent -sets {SS1a SS1b} # equivalent supply sets 什么意思
07-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值