ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

失配树

2021-10-29 20:36:10  阅读:159  来源: 互联网

标签:ch return int ll Maxn 失配 define


名字看起来挺高级的,然而其实就是 \(\text{KMP}\) 上树啦。

我们将每个点的 \(nex[i]\) 与 \(i\) 连边,那么最终 \(border\) 关系会形成一棵树,之后就可以在树上搞事情啦!

P5829 【模板】失配树

这题比较裸,直接根据定义建树之后对于两个前缀求出在 \(fail\) 树上的最近公共祖先即可。

$\texttt{code}$
// Author:A weak man named EricQian
#include<bits/stdc++.h>
using namespace std;
#define infll 0x7f7f7f7f7f7f7f7f
#define inf 0x3f3f3f3f
#define Maxn 1000005
#define Maxpown 22
typedef long long ll;
inline int rd()
{
	 int x=0;
	 char ch,t=0;
	 while(!isdigit(ch = getchar())) t|=ch=='-';
	 while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	 return x=t?-x:x;
}
int n,q,tot;
int pre[Maxn],dep[Maxn];
int fa[Maxn][Maxpown];
char s[Maxn];
inline int query(int x,int y)
{
	 if(dep[x]>dep[y]) swap(x,y);
	 for(int i=20;i>=0;i--) if(dep[fa[y][i]]>=dep[x]) y=fa[y][i];
	 if(x==y) return fa[x][0];
	 for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	 return fa[x][0];
}
int main()
{
	 //ios::sync_with_stdio(false); cin.tie(0);
	 //freopen(".in","r",stdin);
	 //freopen(".out","w",stdout);
	 scanf("%s",s+1),n=strlen(s+1);
	 for(int i=2,j=0;i<=n;i++)
	 {
	 	 while(j && s[i]!=s[j+1]) j=pre[j];
	 	 if(s[i]==s[j+1]) j++;
	 	 pre[i]=j,fa[i][0]=j,dep[i]=dep[j]+1;
	 }
	 for(int i=1;i<=20;i++)
	 	 for(int j=1;j<=n;j++)
	 	 	 fa[j][i]=fa[fa[j][i-1]][i-1];
	 q=rd();
	 for(int i=1,x,y;i<=q;i++) x=rd(),y=rd(),printf("%d\n",query(x,y));
	 //fclose(stdin);
	 //fclose(stdout);
	 return 0;
}

小插曲:(小声)

ZCETHAN 告诉 EricQian 这一题用失配树做,EricQian 立刻表示它不会失配树。【过了 5 秒】EricQian 大声喊道:我发明了失配树!

CF432D Prefixes and Suffixes

题意:给你一个长度为 \(n\) 的长字符串,“完美子串”既是它的前缀也是它的后缀,求“完美子串”的个数且统计这些子串的在长字符串中出现的次数。

我们发现对于一个前缀的出现个数其实就是:(难以表述直接用伪代码列出了)

len=这个前缀的长度 
for(int i=1;i<=n;i++)
	 for(int j=i;j;j=nex[j])
	 	 ans+=(j==len)?1:0;

之后我们发现对于同一个前缀它可能被访问多次,这个可以直接倒换循环顺序实现 \(O(n)\) 解决。(于是你就发明的失配树)

$\texttt{code}$
// Author:A weak man named EricQian
#include<bits/stdc++.h>
using namespace std;
#define infll 0x7f7f7f7f7f7f7f7f
#define inf 0x3f3f3f3f
#define Maxn 100005
typedef long long ll;
inline int rd()
{
	 int x=0;
	 char ch,t=0;
	 while(!isdigit(ch = getchar())) t|=ch=='-';
	 while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	 return x=t?-x:x;
}
inline ll maxll(ll x,ll y){ return x>y?x:y; }
inline ll minll(ll x,ll y){ return x<y?x:y; }
inline ll absll(ll x){ return x>0ll?x:-x; }
inline ll gcd(ll x,ll y){ return (y==0)?x:gcd(y,x%y); }
int n,tot;
int nex[Maxn],cnt[Maxn];
int ans[Maxn][2];
char s[Maxn];
int main()
{
	 //ios::sync_with_stdio(false); cin.tie(0);
	 //freopen(".in","r",stdin);
	 //freopen(".out","w",stdout);
	 scanf("%s",s+1),n=strlen(s+1);
	 for(int i=2,j=0;i<=n;i++)
	 {
	 	 while(j && s[i]!=s[j+1]) j=nex[j];
	 	 if(s[i]==s[j+1]) j++;
	 	 nex[i]=j;
	 }
	 for(int i=n;i>=1;i--) cnt[i]++,cnt[nex[i]]+=cnt[i];
//	 for(int i=1;i<=n;i++)
//	 	 for(int j=i;j;j=nex[j]) cnt[j]++;
	 ans[++tot][0]=n,ans[tot][1]=1;
	 for(int i=nex[n];i;i=nex[i])
	 	 ans[++tot][0]=i,ans[tot][1]=cnt[i];
	 printf("%d\n",tot);
	 for(int i=tot;i>=1;i--) printf("%d %d\n",ans[i][0],ans[i][1]);
	 //fclose(stdin);
	 //fclose(stdout);
	 return 0;
}

P2375 [NOI2014] 动物园

这道题可以根本不用往失配树去理解,我们发现每一次最长合法 \(\text{border}\) 的长度最多只会增加 \(1\),那么直接暴力跑 \(\text{KMP}\) 并及时处理 \(\text{border}\) 长度大于一半的情况即可。

一些正确性证明:如果这个位置的 \(\text{border}\) 长度大于了 \(\frac{i}{2}\),这个 \(\text{border}\) 的后缀起始点将永远不再会成为答案,因此跳出这个 \(\text{border}\) 一定是对的。

$\texttt{code}$
// Author:A weak man named EricQian
#include<bits/stdc++.h>
using namespace std;
#define infll 0x7f7f7f7f7f7f7f7f
#define inf 0x3f3f3f3f
#define Maxn 1000005
#define mod 1000000007
typedef long long ll;
inline int rd()
{
	 int x=0;
	 char ch,t=0;
	 while(!isdigit(ch = getchar())) t|=ch=='-';
	 while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	 return x=t?-x:x;
}
inline ll maxll(ll x,ll y){ return x>y?x:y; }
inline ll minll(ll x,ll y){ return x<y?x:y; }
inline ll absll(ll x){ return x>0ll?x:-x; }
inline ll gcd(ll x,ll y){ return (y==0)?x:gcd(y,x%y); }
int n,ans;
char s[Maxn];
int nex[Maxn],dep[Maxn];
int main()
{
	 //ios::sync_with_stdio(false); cin.tie(0);
	 //freopen(".in","r",stdin);
	 //freopen(".out","w",stdout);
	 int T=rd();
	 while(T--)
	 {
	 	 scanf("%s",s+1),n=strlen(s+1),ans=1;
	 	 dep[1]=1;
	 	 for(int i=2,j=0;i<=n;i++)
	 	 {
	 	 	 while(j && s[i]!=s[j+1]) j=nex[j];
	 	 	 if(s[i]==s[j+1]) j++;
	 	 	 nex[i]=j,dep[i]=dep[j]+1;
		 }
		 for(int i=2,j=0;i<=n;i++)
		 {
		 	 while(j && s[i]!=s[j+1]) j=nex[j];
		 	 if(s[i]==s[j+1]) j++;
		 	 while(j*2>i) j=nex[j];
		 	 ans=1ll*ans*(1ll*dep[j]+1ll)%mod;
		 }
		 printf("%d\n",ans);
	 }
	 //fclose(stdin);
	 //fclose(stdout);
	 return 0;
}

P3426 [POI2005]SZA-Template

咕咕

标签:ch,return,int,ll,Maxn,失配,define
来源: https://www.cnblogs.com/EricQian/p/15482551.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有