题意:统计一段字符串中形如UVU的子串个数(其中V的长度固定为g). 问题等价于求满足$g+1\leqslant |j-i|\leqslant g+LCP(i,j)$的后缀(i,j)的对数,即$\sum\limits_{i<j}[g+1\leqslant |j-i|\leqslant g+LCP(i,j)]$. 由于将原串反转后的后缀(i,j)的LCP等于其在原串的后缀自动机上对应结点的LCA的最大长度,可以枚举LCA,在fail树上将right数组启发式合并即可. #include<bits…
国际惯例的题面:考虑我们求解出字符串uvu第一个u的右端点为i,第二个u的右端点为j,我们需要满足什么性质?显然j>i+L,因为我们选择的串不能是空串.另外考虑i和j的最长公共前缀(也就是说其parent树上lca的len),为了保证他们相同,我们需要:j-len>=i-L.整理一下,如果我们已知i,j需要在区间[i+L+1,i+L+len]中.如果我们已知j,i需要在区间[j-L-len,j-L-1]中.于是我们可以写n^2暴力了:暴力维护parent上每个节点的right集合,对于每个i,…
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直接对\(A\)串建SAM,线段树合并维护一下siz就行了 #include<bits/stdc++.h> using namespace std; const int MAXN = 4e5 + 10, SS = 1e7 + 10; int N, M; char S[MAXN], T[MAXN];…
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下最长能匹配的位置,对于每个以\(i\)位置为右端点的询问我们需要找到\(len\)最小的状态满足\(len[sta] >= pr - pl + 1\)(这部分把每个以\(i\)为端点的询问排序后暴力跳即可,复杂度\(O(n \sqrt{n})\)).那么现在的问题就是对于每个状态,如何知道他在每个\…
题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1, len_x]\),方案数就相当于是从\(siz_x\)里面选两个,也就是\(\frac{siz_x (siz_x - 1)}{2}\) 直接拿线段树维护一下,标记永久化一下炒鸡好写~ #include<bits/stdc++.h> #define int long long #define L…
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代表的最短/最长后缀的位置为\(l, r\)(l在r的右边) 那么对于区间\(r - l\)内的\(x\)位置,可以用\(t - x+1\)更新答案 对于区间\(l - t\)内的位置,可以用\(l\)更新答案 这两种情况不好一起弄(因为第一种情况肯定要把\(x\)提出来),那么直接开两棵线段树就行了…
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n-1)/2. 答案=重复子串个数-相邻或相交重复子串个数. 前者单调栈直接求解,注意细节,重点在后者. 由于是有关相交的计数问题,考虑类似[NOI2016]优秀的拆分的设关键点的做法. 枚举两个串的偏移量k,每k个位置设一个关键点,我们需要保证任意两个相距为k的重复子串都在且仅在它们覆盖的第一个关键…
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能取就取是最优的 我们先用后缀自动机\(+\)线段树合并求出自动机上每一个节点的\(endpos\)集合.如果\(L\)较大的时候,我们考虑二分找到第一个端点,再找下一个--这样在线段树上找的总次数是\({n\over L}\),复杂度为\(O({n\over L}\log n)\) 但是\(L\)较…
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define N 100010 map <int,int> son[N<<1]; char str[N]; int root[N<<1],pla[N],len; int tot=1,last=1,pre[N<<1],dis[N<<1],in[N<<1]…
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我们必然取\(s[a..b]\)的一个子串\(s[x..b]\),因为求最长公共前缀取长了不会影响答案. 那么如果我们二分答案\(mid\),就变成了每次判断原串第\(c\)个后缀长度为\(mid\)的前缀是否是原串第\(a\sim b-mid+1\)个后缀中某一后缀的前缀. 后缀自动机+线段树合并…