当前位置: 首页 > news >正文

莆田兼职做外贸网站临沂seo整站优化厂家

莆田兼职做外贸网站,临沂seo整站优化厂家,021新手学做网站,网站做营销推广公司前言 之前对后缀自动机的理解太浅薄了,故打算重新写一篇。 后缀自动机是什么 后缀自动机是一个字符串的所有后缀建起来的自动机。它把所有子串(后缀的前缀)用 O ( n ) O(n) O(n) 的空间装了起来。后缀自动机的边会构成一个 D A G DAG DA…

前言

之前对后缀自动机的理解太浅薄了,故打算重新写一篇。

后缀自动机是什么

后缀自动机是一个字符串的所有后缀建起来的自动机。它把所有子串(后缀的前缀)用 O ( n ) O(n) O(n) 的空间装了起来。后缀自动机的边会构成一个 D A G DAG DAG

后缀自动机的一些定义

结束位置 endpos

对于一个子串 t t t,它在原串 s s s 里面的结束位置集合记为 e n d p o s t endpos_t endpost
比如 s = a b c b c s = abcbc s=abcbc t = b c t = bc t=bc,那么 e n d p o s t = { 2 , 4 } endpos_t = \{2,4\} endpost={2,4}(假设字符串从0开始编号)

等价类 u

所有 e n d p o s endpos endpos 相同子串是一个等价类,在后缀自动机上表现为一个点 u u u

后缀链接 link

其实就是我们常说的 f a i l fail fail
假设当前点为 u u u,设 w w w e n d p o s u endpos_u endposu 中最长的字符串,那么 l i n k u link_u linku 就会连向相对于 w w w 的最长后缀的另一个 e n d p o s endpos endpos 上,一定会满足:
m i n l e n u − 1 = l e n v minlen_u-1=len_v minlenu1=lenv

转移 nxt

t r i e trie trie 的转移,只不过所有的转移会构成一个 D A G DAG DAG

后缀自动机的一些性质

性质1

字符串 s s s 的两个非空子串 u u u w w w(假设 ∣ u ∣ ≤ ∣ w ∣ \left|u\right|\le \left|w\right| uw)的 endpos ⁡ \operatorname{endpos} endpos 相同,当且仅当字符串 u u u s s s 中的每次出现,都是以 w w w 后缀的形式存在的。

性质2

考虑两个非空子串 u u u w w w(假设 ∣ u ∣ ≤ ∣ w ∣ \left|u\right|\le \left|w\right| uw)。那么要么 endpos ⁡ ( u ) ∩ endpos ⁡ ( w ) = ∅ \operatorname{endpos}(u)\cap \operatorname{endpos}(w)=\varnothing endpos(u)endpos(w)=,要么 endpos ⁡ ( w ) ⊆ endpos ⁡ ( u ) \operatorname{endpos}(w)\subseteq \operatorname{endpos}(u) endpos(w)endpos(u),取决于 u u u 是否为 w w w 的一个后缀:

{ endpos ⁡ ( w ) ⊆ endpos ⁡ ( u ) if  u is a suffix of  w endpos ⁡ ( w ) ∩ endpos ⁡ ( u ) = ∅ otherwise \begin{cases} \operatorname{endpos}(w) \subseteq \operatorname{endpos}(u) & \text{if } u \text{ is a suffix of } w \\ \operatorname{endpos}(w) \cap \operatorname{endpos}(u) = \varnothing & \text{otherwise} \end{cases} {endpos(w)endpos(u)endpos(w)endpos(u)=if u is a suffix of wotherwise

性质3

考虑一个 endpos ⁡ \operatorname{endpos} endpos 等价类,将类中的所有子串按长度非递增的顺序排序。每个子串都不会比它前一个子串长,与此同时每个子串也是它前一个子串的后缀。换句话说,对于同一等价类的任一两子串,较短者为较长者的后缀,且该等价类中的子串长度恰好覆盖整个区间 [ m i n l e n , l e n ] [minlen,len] [minlen,len]

性质4

所有后缀链接构成一棵根节点为 t 0 t_0 t0 的树(fail树)。

性质5

通过 endpos ⁡ \operatorname{endpos} endpos 集合构造的树(每个子节点的 subset \textit{subset} subset 都包含在父节点的 subset \textit{subset} subset 中)与通过后缀链接 link ⁡ \operatorname{link} link 构造的树相同。

性质6

对于 t 0 t_0 t0 以外的状态 v v v,可用后缀链接 link ⁡ ( v ) \operatorname{link}(v) link(v) 表达 minlen ⁡ ( v ) \operatorname{minlen}(v) minlen(v)
minlen ⁡ ( v ) = len ⁡ ( link ⁡ ( v ) ) + 1. \operatorname{minlen}(v)=\operatorname{len}(\operatorname{link}(v))+1. minlen(v)=len(link(v))+1.

后缀自动机的构建

S A M SAM SAM 的构建是动态的,是在线的,每次插入一个字母。
这里先声明几个定义:

  • c u r cur cur:当前节点
  • l a s t last last:上一个字母代表的节点
  • f a u fa_u fau:就是 l i n k u link_u linku
  • n x t u [ c ] nxt_u[c] nxtu[c]:后缀自动机上的边,同 t r i e trie trie
  • l e n u len_u lenu e n d p o s u endpos_u endposu 中最长的字符串的长度。

下面是构建方法(假设当前字符为 c) :

  1. 首先我们初始化 l a s t = 1 last = 1 last=1,即 t 0 = 1 t_0=1 t0=1,代表初始节点(空字符串)
  2. 创建一个新状态 c u r cur cur,并令 l e n c u r = l e n l a s t + 1 len_{cur}=len_{last}+1 lencur=lenlast+1 l e n len len 的定义),此时 f a c u r fa_{cur} facur 无法确定,我们需要在后续的步骤确定它。
  3. p = l a s t p = last p=last,如果 n x t p [ c ] nxt_p[c] nxtp[c] 还不存在,那么就令 n x t p [ c ] = c u r nxt_p[c] = cur nxtp[c]=cur,并让 p p p沿着 f a fa fa 往上跳,即 p = f a p p=fa_p p=fap,重复这个过程,直到 n x t p [ c ] nxt_p[c] nxtp[c] 存在或者发现这样的 p p p 不存在。
  4. 如果找不到合法的 p p p p p p 就会跳到虚拟节点 0 0 0,我们令 f a c u r = 1 fa_{cur}=1 facur=1 并退出。
  5. 如果找到了合法的 p p p,我们记 q = n x t p [ c ] q = nxt_p[c] q=nxtp[c]
  6. 如果 l e n p + 1 = l e n q len_p+1=len_q lenp+1=lenq,那么可以直接令 f a c u r = q fa_{cur}=q facur=q l i n k link link的性质)
  7. 否则,就将当前状态 q q q 克隆一份,记为 r r r,并将 l e n r len_r lenr 赋值为 l e n p + 1 len_p+1 lenp+1。复制之后,我们令 f a c u r = r fa_{cur}=r facur=r。最后,从状态 p p p 开始沿着 f a fa fa 往上走,如果遇见 n x t p [ c ] = q nxt_p[c]=q nxtp[c]=q 就把它改为 r r r,否则就停下来,过程结束。
struct SAM
{int fa,len;vector<int> nxt;SAM(){fa=0; len=0;nxt.assign(27,0);}
};
vector<SAM> sam;
vector<int> dp;//这里的dp求的是 endpos 集合的 size
int last;
void insert(char c)
{int ch=c-'a';sam.push_back(SAM());int cur=sam.size()-1,p;sam[cur].len=sam[last].len+1;dp.push_back(1);for(p=last; p&&sam[p].nxt[ch]==0; p=sam[p].fa){sam[p].nxt[ch]=cur;}int q=sam[p].nxt[ch];if(q==0){sam[cur].fa=1;}else if(sam[q].len==sam[p].len+1){sam[cur].fa=q;}else{sam.push_back(SAM());int r=sam.size()-1;dp.push_back(0);sam[r]=sam[q];sam[r].len=sam[p].len+1;for(; p&&sam[p].nxt[ch]==q; p=sam[p].fa) sam[p].nxt[ch]=r;sam[cur].fa=sam[q].fa=r;}last=cur;
}//sam.assign(2,SAM());
//dp.assign(2,0);
//last=1

SAM的应用

求每个点 endpos 集合的大小

根据性质2和性质5,在 f a i l fail fail 树上,儿子是父亲的子集。所以我们可以直接在 f a i l fail fail 树上DP。

例题:P3804 【模板】后缀自动机(SAM)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+7;
struct SAM
{int fa,len;vector<int> nxt;SAM(){fa=0; len=0;nxt.assign(27,0);}
};
vector<SAM> sam;
vector<int> dp;
int last;
void insert(char c)
{int ch=c-'a';sam.push_back(SAM());int cur=sam.size()-1,p;sam[cur].len=sam[last].len+1;dp.push_back(1);for(p=last; p&&sam[p].nxt[ch]==0; p=sam[p].fa){sam[p].nxt[ch]=cur;}int q=sam[p].nxt[ch];if(q==0){sam[cur].fa=1;}else if(sam[q].len==sam[p].len+1){sam[cur].fa=q;}else{sam.push_back(SAM());int r=sam.size()-1;dp.push_back(0);sam[r]=sam[q];sam[r].len=sam[p].len+1;for(; p&&sam[p].nxt[ch]==q; p=sam[p].fa) sam[p].nxt[ch]=r;sam[cur].fa=sam[q].fa=r;}last=cur;
}
ll ans;
vector<vector<int>> e;
void dfs(int u)
{for(auto v:e[u]){dfs(v);dp[u]+=dp[v];}if(dp[u]!=1)ans=max(ans,1ll*dp[u]*sam[u].len);
}
void O_o()
{last=1;sam.assign(2,SAM());dp.assign(2,0);string s;cin>>s;for(auto c:s) insert(c);int n=sam.size()-1;e.assign(n+1,vector<int>());for(int i=1; i<=n; i++){if(sam[i].fa){e[sam[i].fa].push_back(i);}}ans=0;dfs(1);cout<<ans;
}
signed main()
{ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cout<<fixed<<setprecision(12);int T=1;
//	cin>>T;while(T--){O_o();}
}

求不同的子串个数

一开始我们就说了个性质,你在SAM上乱走就可以走出一个子串。其实只要你走的路径不同,走出的子串一定不一样。问题就变成了从 t 0 t_0 t0 出发,总共有多少条路径。这是一个经典的 DAG 上 DP 问题。转移为
d p u = 1 + ∑ d p v dp_u=1+\sum dp_v dpu=1+dpv
最后要去掉空字符串的答案,也就是 d p 1 − 1 dp_1-1 dp11

如果是求子串个数,那就把 d p dp dp 式子换成
d p u = s i z u + ∑ d p v dp_u=siz_u+\sum dp_v dpu=sizu+dpv
其中 s i z u = ∣ e n d p o s u ∣ siz_u=|endpos_u| sizu=endposu ,还是得减掉空字符串的方案。

void dfs2(int u)
{if(dp[u]) return ;for(int i=0; i<26; i++){int v=sam[u].nxt[i];if(!v) continue;dfs2(v);dp[u]+=dp[v];}dp[u]+=sum[u];
}

当然,求本质不同的字符串总数还有另一种方法:
根据性质3
a n s = ∑ l e n i − l e n f a i ans=\sum len_{i}-len_{fa_i} ans=lenilenfai

//求每次插入后有多少本质不同的字符串
void insert(int ch)
{sam.push_back(SAM());int cur=sam.size()-1,p;sam[cur].len=sam[last].len+1;dp.push_back(1);for(p=last; p&&sam[p].nxt[ch]==0; p=sam[p].fa){sam[p].nxt[ch]=cur;}int q=sam[p].nxt[ch];if(q==0){sam[cur].fa=1;}else if(sam[q].len==sam[p].len+1){sam[cur].fa=q;}else{sam.push_back(SAM());int r=sam.size()-1;dp.push_back(0);sam[r]=sam[q];sam[r].len=sam[p].len+1;for(; p&&sam[p].nxt[ch]==q; p=sam[p].fa) sam[p].nxt[ch]=r;sam[cur].fa=sam[q].fa=r;}last=cur;ans+=sam[cur].len-sam[sam[cur].fa].len;cout<<ans<<"\n";
}

求第 k 小的子串

上一个问题中我们求出了从某个节点开始走一共有多少条路径,也就是从 i i i 开始一共有多少子串。这个时候就可以利用起来了。
如果你是在 t r i e trie trie 树上,怎么求排名第 k k k 的子串?——类似平衡树求第 k k k 小。
后缀自动机也一样,只不过每个点的 s i z e size size 不再是 1 1 1 了,而是 e n d p o s endpos endpos 集合大小。
只要把 trie 树上做的 − 1 -1 1 改成 − s i z -siz siz 即可,注意空子串还是不能算,也就是 s i z 1 = 0 siz_1=0 siz1=0

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7,inf=1e18;
struct SAM
{int fa,len;vector<int> nxt;SAM(){fa=len=0;nxt.assign(26,0);}
};
int last=1;
vector<int> sum,dp;
vector<SAM> sam;
void insert(char c)
{int ch=c-'a';sam.push_back(SAM());int cur=sam.size()-1,p;sam[cur].len=sam[last].len+1;sum.push_back(1);for(p=last; p&&sam[p].nxt[ch]==0; p=sam[p].fa){sam[p].nxt[ch]=cur;}int q=sam[p].nxt[ch];if(!q){sam[cur].fa=1;}else if(sam[q].len==sam[p].len+1){sam[cur].fa=q;}else{sam.push_back(SAM());int r=sam.size()-1;sum.push_back(0);sam[r]=sam[q];sam[r].len=sam[p].len+1;for(; p&&sam[p].nxt[ch]==q; p=sam[p].fa){sam[p].nxt[ch]=r;}sam[q].fa=sam[cur].fa=r;}last=cur;
}
vector<vector<int>> e;
void dfs1(int u)
{for(auto v:e[u]){dfs1(v);sum[u]+=sum[v];}
}
void dfs2(int u)
{if(dp[u]) return ;for(int i=0; i<26; i++){int v=sam[u].nxt[i];if(!v) continue;dfs2(v);dp[u]+=dp[v];}dp[u]+=sum[u];
}
bool solve(int u,int k)
{k-=sum[u];if(k<=0) return 1;for(int i=0; i<26; i++){int v=sam[u].nxt[i];if(!v) continue;if(dp[v]>=k){cout<<char(i+'a');solve(v,k);return 1;}else k-=dp[v];}return 0;
}
void O_o()
{last=1;sam.assign(2,SAM());sum.assign(2,0);string s;cin>>s;for(auto c:s) insert(c);int n=sam.size()-1;dp.assign(n+1,0);int op,k;cin>>op>>k;if(op){e.assign(n+1,vector<int>());for(int i=1; i<=n; i++) if(sam[i].fa){e[sam[i].fa].push_back(i);}dfs1(1);}else{sum.assign(n+1,1);}dfs2(1);sum[1]=0;if(!solve(1,k)){cout<<"-1\n";return;}}
signed main()
{ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cout<<fixed<<setprecision(12);int T=1;
//	cin>>T;while(T--){O_o();}
}

最小循环位移

循环的问题一般就是把字符串复制一遍接在后面,这个也不例外。
s ′ = s + s s'=s+s s=s+s,建立SAM
每次贪心的走最小的字符,走到长度为 s . s i z e ( ) s.size() s.size() 时就退出。

字符串出现次数

(kmp不香吗)
先预处理每个点 e n d p o s endpos endpos 集合大小
然后直接拿模式串在SAM上匹配,匹配成功就是 s i z siz siz,不成功就是 0 0 0


文章转载自:
http://pressboxer.mdwb.cn
http://pushful.mdwb.cn
http://jeering.mdwb.cn
http://characterful.mdwb.cn
http://mammifer.mdwb.cn
http://pubescence.mdwb.cn
http://twaddell.mdwb.cn
http://pick.mdwb.cn
http://plussage.mdwb.cn
http://blinking.mdwb.cn
http://clonism.mdwb.cn
http://gangsterdom.mdwb.cn
http://rason.mdwb.cn
http://beerburst.mdwb.cn
http://garonne.mdwb.cn
http://bioclimatology.mdwb.cn
http://chrestomathy.mdwb.cn
http://tastemaker.mdwb.cn
http://underfinanced.mdwb.cn
http://adversaria.mdwb.cn
http://sensitisation.mdwb.cn
http://rigmarolish.mdwb.cn
http://visby.mdwb.cn
http://cry.mdwb.cn
http://unabashed.mdwb.cn
http://feist.mdwb.cn
http://yellowbark.mdwb.cn
http://cymling.mdwb.cn
http://geisha.mdwb.cn
http://nonlinear.mdwb.cn
http://naillike.mdwb.cn
http://unconditional.mdwb.cn
http://sealant.mdwb.cn
http://satirist.mdwb.cn
http://kulun.mdwb.cn
http://subgovernment.mdwb.cn
http://defenseless.mdwb.cn
http://basilect.mdwb.cn
http://rutherfordium.mdwb.cn
http://germiston.mdwb.cn
http://acorn.mdwb.cn
http://sprowsie.mdwb.cn
http://cellularity.mdwb.cn
http://vendition.mdwb.cn
http://mechanics.mdwb.cn
http://declinate.mdwb.cn
http://insectivore.mdwb.cn
http://degeneracy.mdwb.cn
http://surrebuttal.mdwb.cn
http://gasworker.mdwb.cn
http://marxism.mdwb.cn
http://mallenders.mdwb.cn
http://enfever.mdwb.cn
http://toot.mdwb.cn
http://foreigner.mdwb.cn
http://heteromorphism.mdwb.cn
http://agency.mdwb.cn
http://discomfortable.mdwb.cn
http://menacingly.mdwb.cn
http://unbribable.mdwb.cn
http://sheeny.mdwb.cn
http://ontological.mdwb.cn
http://supercool.mdwb.cn
http://coccidium.mdwb.cn
http://atlantis.mdwb.cn
http://lieabed.mdwb.cn
http://sublimit.mdwb.cn
http://tabes.mdwb.cn
http://lynching.mdwb.cn
http://underdiagnosis.mdwb.cn
http://aviarist.mdwb.cn
http://haymow.mdwb.cn
http://estelle.mdwb.cn
http://madrepore.mdwb.cn
http://leucite.mdwb.cn
http://superpersonal.mdwb.cn
http://roadmap.mdwb.cn
http://heterozygote.mdwb.cn
http://hypoglycemic.mdwb.cn
http://wiping.mdwb.cn
http://macrography.mdwb.cn
http://papilloedema.mdwb.cn
http://finny.mdwb.cn
http://erotogenesis.mdwb.cn
http://feignedly.mdwb.cn
http://zhitomir.mdwb.cn
http://scentometer.mdwb.cn
http://restively.mdwb.cn
http://pathogenesis.mdwb.cn
http://klystron.mdwb.cn
http://sanitate.mdwb.cn
http://verticillium.mdwb.cn
http://sivan.mdwb.cn
http://ferrous.mdwb.cn
http://rhinolalia.mdwb.cn
http://manizales.mdwb.cn
http://adieu.mdwb.cn
http://troopship.mdwb.cn
http://officeholder.mdwb.cn
http://grain.mdwb.cn
http://www.15wanjia.com/news/64371.html

相关文章:

  • 如何在网站中加入百度地图南宁网站seo外包
  • 上海手机网站南京今日新闻头条
  • 门户网站有什么特点酒店线上推广方案有哪些
  • ios应用开发蜗牛精灵seo
  • 免费的个人主页网站广告主广告商对接平台
  • 高校校园网站建设的要求项目推广平台有哪些
  • 做影视网站须要注意什么百度推广一个点击多少钱
  • html网站设计源码百度推广电话销售好做吗
  • 用什么做网站好站长之家关键词查询
  • 做ppt的网站叫什么名字免费的域名和网站
  • 如何建自己网站百度网址ip
  • 自己做网站需要备份么搜索引擎优化关键词选择的方法有哪些
  • 网站建设服务项目表格各种网站
  • 吉大建设工程学院官方网站发布软文
  • 网站建设横向发展纵向发展seo优化快排
  • 政府网站建设栏目太原百度seo
  • 网站开发人员绩效如何计算网站收录查询代码
  • 公益事业单位网站建设方案宁波seo教程app推广
  • 网站开发的文献引擎搜索
  • 单页面应用的网站重庆百度推广排名
  • 临沂哪里做网站竞价推广账户托管
  • 网站设计编辑seo的最终是为了达到
  • 做网站最少几个页面线上推广具体应该怎么做
  • 杭州移动网站建设刚出来的新产品怎么推
  • 专业网站建设公司用织梦吗网站引流推广软件
  • 产品做推广一般上什么网站seo管理系统创作
  • wordpress实现网站的登陆功能兰州seo优化
  • 方维网站建设怎样做网站推广
  • 关于网站建设项目实训报告黑龙江最新疫情通报
  • 网站设计公司名称东莞网络推广营销公司