Nowcoder String Class Problems

基础概念

S[i]S[i]:表示字符串 SSii 个位置的字母,下标从 11 开始。
PrefixS[i]PrefixS[i]:表示字符串 SS 的长度为 ii 的前缀,PrefixS[i]=S[1,i]PrefixS[i] = S[1, i]
SuffixS[i]SuffixS[i]:表示字符串 SS 的长度为 ii 的后缀,SuffixS[i]=S[Si+1,S]SuffixS[i] = S[|S| − i + 1, |S|]
BorderBorder:如果字符串 SS 的同长度的前缀和后缀完全相同,即 Prefix[i]=Suffix[i]Prefix[i] = Suffix[i],则称此前缀(后缀)为一个BorderBorder
特殊地,字符串本身也可以是它的 BorderBorder
周期:对于字符串 $S4 和正整数 $p4,如果有 S[i]=S[ip]S[i] = S[i − p],对于 p<iSp < i ≤ |S| 成立,则称 pp 为字符串 SS 的一个周期。
特殊地,p=Sp = |S| 一定是 SS 的周期。
循环节:若字符串 SS 的周期 pp 满足 pSp | |S|,则称 ppSS 的一个循环节。
特殊地,p=Sp = |S| 一定是 SS 的循环节。

重要性质

ppSS 的周期 ⇔ Sp|S| − pSSBorderBorder
传递性:SSBorderBorderBorderBorder 也是 SSBorderBorder
SS 的所有 BorderBorder 等价于求所有前缀的最大 BorderBorder
BorderBorder 的性质
周期定理:若 p,qp, q 均为串 SS 的周期,则 (p,q)(p, q) 也为 SS 的周期。
一个串的 BorderBorder 数量是 O(N)O(N) 个,但他们组成了 O(logN)O(logN) 个等差数列。

KMP

字符串的问题
code

[模板]KMP字符串匹配
code

数一数
长度大的串在长度小的串中出现的次数为00 ,所以非最小的串的答案必定为00
最小的串中,如果不相等,则互相出现在对方的次数为00
否则只存在一种最小串,求这种串出现其他串的次数(kmpkmp)。
code

栗酱的数列
(a1+b1)(a'1+b1)%k = (a'2+b2)%k = …… = (a'm + bm)%k
等价于m1m-1 个等式
(a1+b1)(a'1+b1)%k = (a'2+b2)%k
(a2+b2)(a'2+b2)%k = (a'3+b3)%k
……
(a(m1)+b(m1))(a'(m-1)+b(m-1))%k = (a'm+bm)%k
移项
(a1a2)(a'1-a'2)%k = (b2-b1)%k
(a2a3)(a'2-a'3)%k = (b3-b2)%k
……
(a(m1)am)(a'(m-1)-a'm)%k = (bm-b(m-1))%k
Diffa[i]=a[i]a[i+1],Diffb[i]=b[i+1]b[i]Diffa[i]=a'[i]-a[i+1],Diffb[i]=b[i+1]-b[i]
即在长度为n1n-1DiffaDiffa 数组中找有多少个长度为m1m-1DiffbDiffb 数组匹配。
code

K匹配
SS 中有多少子串包含TT
KMPKMP 求出所有TT 左右端点坐标,对于每个子串TT,求以它为最后一个被包含的TT 的子串数量:左端点坐标×(下一个子串右端点坐标-右端点坐标)。
code

carpet
求最小二维循环周期:分别对行和列求最小循环周期。
求所有行的最小循环周期:求出每行的所有循环周期,取一个最小的公共周期qq
注意:不是所有行的最小循环周期的lcmlcm
列同理。
对于固定大小的矩阵,最小化矩阵最大值:二维单调队列求每个矩阵的最大值,再取最小值即可。
code

字符串Hash

定义:HashHash 是一种单射函数,可以将万物单向映射成一个整数值。
good hash table primes
字符串哈希
code随机双模hash
回文串:
给出一个字符串 SS,每次操作可以删除最末尾的一个字符,最少进行多少次操作可以得到一个回文串。
解法:求出每个前缀和后缀的hashhash 值,每删除一个末尾字符比较当前前缀和后缀的hashhash 值是否相等。

子串字典序比较11
给出一个正整数数组 AA,长度不超过 100,000100, 000,以及 Q100,000Q ≤ 100, 000 次询
问:子串 A[l1,r1]A[l1, r1]A[l2,r2]A[l2, r2] 的字典序大小关系。
解法:二分两个子串的LCPLCPhashhashcheckcheck ,然后比较后面一个字符的大小。O(Qlog(n))O(Qlog(n))

子串字典序比较 22
给出一个正整数数组 AA,长度不超过 100,000100, 000,以及 Q100,000Q ≤ 100, 000 次操
作:
询问:子串 A[l1,r1]A[l1, r1]A[l2,r2]A[l2, r2] 的字典序大小关系。
修改:将 A[x]A[x] 的值替换为 yy
解法:线段树维护hashhash ,区间和并:hash(A+B)=hash(A)*base^\abs(B)+hash(B)
查询仍然二分LCPLCPhashcheckhashcheck

子串字典序比较 33
给出一个正整数数组 AA,长度不超过 100,000100, 000,以及 Q100,000Q ≤ 100, 000 次操
作:
询问:子串 A[l1,r1]A[l1, r1]A[l2,r2]A[l2, r2] 的字典序大小关系。
修改:将区间 [l,r][l, r] 位置的数字值增加 11
解法:区间修改线段树维护hashhash,区间修改:区间hashhash 值加上1+Base^1+Base^2+……+Base^{\abs(A)-1}

E. Kefa and Watch
给出一个数组 AA,进行 QQ 次操作:
询问:pp 是否是区间 [L,R][L, R] 的周期。
修改:将区间 [L,R][L, R] 的数字赋值为 kk
解法:
询问:pp 是否是区间 [L,R][L, R] 的周期,即[L,Rp][L,R-p] 是否是[L,R][L,R]BorderBorder
[L,Rp][L,R-p] 是否等于 [L+p,R][L+p,R]hashhash 检查。
修改:区间修改线段树维护hashhash
code

【模板】扩展 KMP/exKMP(Z 函数)
扩展 KMP(Z 函数)
code

子串查询
ss 做一遍序列自动机,找到任一字符在s[i]s[i] 的后面第一次出现的位置。
每一次询问时从前往后扫tt,在ss 上往后跳。每次询问的时间复杂度为O(t)O(|t|)

快乐的JYY
二分哈希找出AA 的每个位置的最大回文半径(分奇数长度和偶数长度),得到所有的极长回文串O(2n)O(2n)
将每个极长回文串向里缩,得到所有的本质不同回文串O(n)O(n),同时统计出现次数。
同样处理BB
AABB 中相同的回文串出现次数相乘取和。
code

Trie

假的字符串
trie存字符串。
依次判断每个字符串是否可能为最小字符串.
1.若存在前缀字符串,则不可能。
2.对于trie上的每个节点的后继,sis_i 所在的节点一定比其他后继节点小,将sis_i 所在的节点的字符与其他后继节点的字符建边。最后跑一遍拓扑排序判无环即可。
(坑点:卡内存)
code

The XOR Largest Pair
01Trie裸题。
code

奶牛异或
做一遍异或前缀和,然后01Trie。
code

最大异或和
做一遍前缀和,查询的答案可化简为:找一个l1<=p<r1l-1<=p<r-1,使apXa_{p}\\^X 最大,X=anxX=a_n\\^x
可持久化Trie:每个前缀都有一棵Trie,每次只对新增的数建节点,其余部分直接连到前面的Trie的节点上,对每个节点记录一个已有个数cntcnt
查询:从l1l-1r1r-1 的根节点开始,对于XX 的当前位上的数uu ,若cnt[trie[r][1u]]cnt[trie[l][1u]]>=1cnt[trie[r][1-u]]-cnt[trie[l][1-u]]>=1 ,则说明llrr 这个区间内这一位上存在1u1-u ,往1u1-u 走,res+=1<<ires+=1<<i,否则往uu 走。

code

Border树(KMP建树)

定义:
对于一个字符串 SSn=Sn = |S|,它的 BorderBorder 树(也叫 nextnext 树)共有 n+1n + 1 个节点:0,1,2,...,n0, 1, 2, . . . , n
00 是这棵有向树的根。对于其他每个点 1in1 ≤ i ≤ n,父节点为 next[i]next[i]
性质
1.每个前缀 Prefix[i]Prefix[i] 的所有 BorderBorder:节点 ii 到根的链。
2.哪些前缀有长度为 xxBorderBorderxx 的子树。
3.求两个前缀的公共 BorderBorder 等价于求 LCALCA

[模板]失配树
KMPKMP 跑出next[]next[] ,再根据next[]next[] 建立BorderBorder 树。
求最长公共前缀等价于求LCALCA
注意:
1.next[]next[] 中的00 是真实节点,而LCALCA 中的00 是虚拟节点。所以建树时要给每个点的编号加个偏移量11
2.求LCALCA 时可能某个点就是LCALCA,但next[]next[] 是指最大非平凡BorderBorder ,不能等于自身,应该取父节点fa[p][0]fa[p][0]

葫芦和斌斌的字符串1
1122 等价于求SSBorderBorder
TT 的出现次数等价于其子树的大小。
建立BorderBorder 树,从nn 向上爬,直到该节点子树大小大于kk
KMPKMP 和建BorderBorderO(n)O(n),查询O(log2n)O(log_2n)
code

葫芦和斌斌的字符串2
离线把最终BorderBorder 树建出来,求出dfsdfs 序,就可以得到每个节点的子树所构成的dfsdfs 序区间。
每次加一个点就相当于给dfsdfs 序的某个单点加一。
查询一个子树的大小相当于在dfsdfs 序上求这个子树所在区间的和。
找最长的TT 可以二分高度或者链上倍增。
KMPKMP、建BorderBorder 树、求dfsdfsO(n)O(n),单点修改O(log2n)O(log_2n),区间查询O(log2n)O(log_2n)
code

AC自动机

ACAC 自动机 = TrieTrie + KMPKMP
ACAC 自动机基于 TrieTrie,将 KMPKMPBorderBorder 概念推广到多模式串上。
ACAC 自动机是一种离线型数据结构,即不支持增量添加新的字符串。
ACAC 自动机常用于将字符串询问类的问题进行离线处理,也经常与各种DPDP 结合,或是补全成 TrieTrie 图。
广义 borderborder:
推广到两个串:对于两个串 SSTT ,相等的 pp 长度的 SS 的后缀和 TT 的前缀称为一个 borderborder
推广到一个字典:对于串 SS 和一个字典 DD,相等的 pp 长度的 SS 的后缀,和任意一个字典串 TT 的前缀称为一个 borderborder
失配(FailFail)指针:
对于 TrieTrie 中的每一个节点(即某个字典串的前缀),它与 TrieTrie 中所有串的最大 BorderBorder 即为失配指针。
类似与 KMPKMPBorderBorder,任意节点的 BorderBorder 长度减一,一定是父节点的BorderBorder
因此可以通过遍历父节点的失配指针链来求解。
因此在求失配指针的时候,一定要按长度从小到大来求,即 bfsbfs
复杂度分析:
类似于 KMPKMP 的势能分析方法,势能总量等于 TrieTrie 的节点总数,因此复杂度为线性的。

模板】AC 自动机(二次加强版)
优化:
1.

1
2
if(!trie[u][i])
trie[u][i]=trie[fail[u]][i]

2.拓扑优化建图,防止查询每个模式的次数时只能暴力跳failfail ,最坏达到O(ST)O(|S|·|T|)
code
3.failfail 子树求和。拓扑建图是从树枝往树根推,子树求和是利用dfsdfs 从根往下。
code

string
ACAC 自动机是离线数据结构,不支持动态修改,考虑离线。
先把最终的failfail 树建出来,求出dfsdfs 序。
修改只要修改两个标记节点即可(add(in[p],1)),add(out[p],1)(add(in[p],1)),add(out[p],-1)
查询就是在trietrie 上遍历匹配这个字符串时,对于每个到达的节点,加上failfail 上这个节点到根上所有节点的标记值。
单点修改,区间查询,树状数组维护dfsdfs 序上每个点到根的终止节点数。
code

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2023-2025 Shiki
  • Visitors: | Views: