字符串题目合集
压缩
题意:一段由相同字符串重复而来的字符串可以写成次方的形式。给定
DP + KMP
同时可以枚举断点。
最后,可以让它自己压缩(只有在
判断两个字符串 是否循环同构。
令
Periods of Words
题意:若
容易发现,对于一个固定的
因此跑一遍 KMP,可以求出每个前缀原始的
-
失配树找最高级祖先。
-
类似并查集路径压缩。因为如果当前位置的
不是这个位置最小的 ,那么任何时刻都不会用到这个位置的 。于是每到一个新位置,直接暴力把当前位置的 一直递归到最小即可。
寻找 的最长前缀是 的子串。
SAM 直接冲
我们可以用 KMP 这个优雅的算法。令
观察得知,KMP 当匹配到母串的第
答案就是
求 每个前缀的出现次数。
SAM 直接冲
-
KMP 方法。先对
做一遍 KMP。对于前缀 ,只要看 里有多少个 即可。不要忘了加上自己开头出现了一次。 -
Z 函数方法。先求出
函数。对于前缀 ,只要看 里有多少个 即可。不要忘了加上自己开头出现了一次。
Extend to Palindrome
简化题意:求最长回文后缀。
令
求本质不同子串数。
SAM 直接冲
考虑增量算法:已经处理了字符串
不过因为每新加入一个字符就要重新求一遍,复杂度
Pal-Palindrome
结论:两个回文串
那么弄一个类似桶的东西即可。
x-prime substrings
题意比较复杂,点进去看。
观察到
Mysterious Code
题意:给定一个字符串
给
Bracket Substring
题意:求有多少个长为
给
Resource Archiver
题意:给定
病毒串总长
法一:把所有串丢进 AC 自动机里,给病毒串结点打病毒标记,给资源串结点标记编号。
但是空间会爆。
法二:同样把所有串丢进 AC 自动机里,同样打标记,
对于自动机上一个点到所有点的最短路,可以用一次最短路解决;于是跑
转移就枚举下一个去哪个资源串,注意就算是去过的资源串也要考虑。
Median Replace 及其题解
CF1286E
题意:维护一个字符串
如果
CF 3200 分
(为了契合这一题的题意,下面 border 的描述是包括了一整个串的)
考虑增量算法。这一次加入的是
因为
但是维护 border 不好做,于是考虑用一个数据结构维护当前 border 的权值的集合。(这一步也不知道怎么想出来的)
记这个集合为
-
一个 border 为
,若 ,应该删除这个 border; -
在所有剩下的 border 里,所有权值
的 border 都把权值改成 ,因为是取 。 -
如果
,会新增一个 border 为 。
我们可以用一个 map 维护这些权值。
-
如何快速找到
的 border?找 border,最先的想法就是在失配树(
树)上找,然后在 map 里依次删除。但是如果 这样跳下去必定超时。考虑记录一个
数组。 表示在 中(失配树的祖先中),最近的与 期望的字符不同的是哪个。例如
,既有 作为 border,又有 作为 border。但是 期望的字符是 , 期望的字符是 , 就记录为 。(期望的字符,就是在 后面添加哪个字符,还能保持是 border)如果有这个数组,可以这么做:令
,若 依然是 border, ,这可以保证 会跳到一个不再是 border 的;否则 。
这样每跳两步就至少删除一个 border,因为 border 总数是 的,所以跳的次数是均摊 的。 -
权值
的全部改成 。利用 map.end() 倒序遍历 map,直到所有
的都改完。因为每进行一次修改,有至少一个 border 合并到权值为 的里面去了,而 border 总数 的,所以至多修改 次,总共复杂度 。 -
新增一个长度为
的 border。这个很简单。
于是复杂度
求一个 border 的权值就是简单的 RMQ 问题。最需要注意的是 map 中等于
Frequency of Strings
题意:
给你一个字符串
保证
记询问串长度和为
因此所有出现位置
对所有询问串建立 AC 自动机,在 keyword 结束的结点维护一个 vector,用来保存结束位置。把
void mdf(string &s) {
int x = 1;
for (int i = 0; i < s.size(); i++) {
x = abs(a[x].e[cd(s[i])]);
int p = x;
if (a[x].ed == 0)
p = a[p].nxtkey;
while (p != 1) {
a[p].v.push_back(i);
p = a[p].nxtkey;
}
}
}
回答询问的时候,找到对应结点,取出 vector 里的结束位置,枚举从哪个结束位置到哪个结束位置取个
int len = 0x3f3f3f3f;
for (int j = 0; j + k[i] - 1 < t.a[t.nd[i]].v.size(); j++)
len = min(len, (int)(t.a[t.nd[i]].v[j + k[i] - 1] - (t.a[t.nd[i]].v[j] - m[i].size() + 1) + 1));
cout << len << endl;
CF868D:Huge Strings
题意:给定初始
难点在于拼接的字符串长度会很大。考虑所有长度为
发现每次拼接产生新的子串,必然在于拼接处。一共拼接
因此最大答案不会超过
因为一次拼接至多贡献出长度为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!