常用算法代码模板与题单 | F - 字符串
【字符串入门】
算法模板
s.find(str, pos) // 从pos开始查找子串str第一次出现的位置
s.rfind(str, pos) // 从pos开始向左查找子串最后一次出现的位置
s.substr(pos, len) // 从pos开始截取长度为len的子串
s.substr(pos) // 从pos截取到末尾
s.replace(pos, len, str) // 将pos开始的len个字符替换为str
算法应用
| 题目来源 | 标题 | 难度 | 星级 | 考察算法 | 一句话思路总结 |
|---|---|---|---|---|---|
| 洛谷 | AT_abc335_a 202 3 | 入门 | |||
| 洛谷 | AT_abc336_a Long Loong | 入门 | |||
| 洛谷 | AT_abc337_b Extended ABC | 入门 | |||
| 洛谷 | AT_abc338_a Capitalized | 入门 | |||
| 洛谷 | AT_abc338_b Frequency | 入门 | |||
| 洛谷 | AT_abc339_a TLD | 入门 | |||
| 洛谷 | AT_abc349_b Commencement | 入门 | |||
| 洛谷 | AT_abc375_a Seats | 入门 | |||
| 洛谷 | AT_abc379_b Strawberries | 入门 | |||
| 洛谷 | AT_abc381_b 1122 String | 入门 | |||
| 洛谷 | AT_abc382_b Daily Cookie 2 | 入门 | |||
| 洛谷 | AT_abc384_a aaaadaa | 入门 | |||
| 洛谷 | AT_abc386_b Calculator | 入门 | |||
| 洛谷 | AT_abc391_a Lucky Direction | 入门 | |||
| 洛谷 | AT_abc391_b Seek Grid | 入门 | |||
| 洛谷 | AT_abc393_a Poisonous Oyster | 入门 | |||
| 洛谷 | AT_abc393_b A..B..C | 入门 | |||
| 洛谷 | AT_abc398_a Doors in the Center | 入门 | |||
| 洛谷 | AT_abc399_a Hamming Distance | 入门 | |||
| 洛谷 | AT_abc402_a CBC | 入门 | |||
| 洛谷 | AT_abc404_b Grid Rotation | 入门 | |||
| 洛谷 | AT_abc409_a Conflict | 入门 | |||
| 洛谷 | AT_abc411_a Required Length | 入门 | |||
| 洛谷 | AT_abc412_b Precondition | 入门 | |||
| 洛谷 | AT_abc413_b cat 2 | 入门 | |||
| 洛谷 | AT_abc414_b String Too Long | 入门 | |||
| 洛谷 | AT_abc416_a Vacation Validation | 入门 | |||
| 洛谷 | AT_abc417_a A Substring | 入门 | |||
| 洛谷 | AT_abc418_a I'm a teapot | 入门 | |||
| 洛谷 | AT_abc418_b You're a teapot | 入门 | |||
| 洛谷 | AT_abc419_a AtCoder Language | 入门 | |||
| 洛谷 | AT_abc422_a Stage Clear | 入门 | |||
| 洛谷 | AT_abc426_a OS Versions | 入门 | |||
| 洛谷 | AT_abc426_b The Odd One Out | 入门 | |||
| 洛谷 | AT_abc436_a o-padding | 入门 | |||
| 洛谷 | AT_abc438_b Substring 2 | 入门 | |||
| 洛谷 | AT_abc441_b Two Languages | 入门 | |||
| 洛谷 | AT_abc443_a Append s | 入门 | |||
| 洛谷 | AT_abc451_a illegal | 入门 | |||
| 洛谷 | AT_abc464_a Decisive Battle | 入门 | ⭐ | 字符串遍历 / 计数 | 遍历字符串 \(S\) 统计字符 E 和 W 的出现次数,分别代表东军和西军士兵人数,直接比较两个计数器大小输出 East 或 West,由于字符串长度为奇数保证结果必不相等,时间复杂度 $O(\ |
| 洛谷 | B2109 统计数字字符个数 | 入门 | |||
| 洛谷 | B2110 找第一个只出现一次的字符 | 入门 | |||
| 洛谷 | B2116 加密的病历单 | 入门 | |||
| 洛谷 | B2117 整理药名 | 入门 | |||
| 洛谷 | B2118 验证子串 | 入门 | |||
| 洛谷 | B2119 删除单词后缀 | 入门 | |||
| 洛谷 | B2120 单词的长度 | 入门 | |||
| 洛谷 | B2121 最长最短单词 | 入门 | |||
| 洛谷 | B2122 单词翻转 | 入门 | |||
| 洛谷 | B2124 判断字符串是否为回文 | 入门 | |||
| 洛谷 | B3674 标题修改 | 入门 | |||
| 洛谷 | B3713 对了,还有花,少女,银河 | 入门 | |||
| 洛谷 | [[B3956 字母求和]] | 入门 | |||
| 洛谷 | [[B4003 移位]] | 入门 | |||
| 洛谷 | [[B4088 最大回文数]] | 入门 | |||
| 洛谷 | B4128 夏令营旗手 | 入门 | |||
| 洛谷 | B4301 数字交换 | 入门 | |||
| 洛谷 | [[B4360 画布剪裁]] | 入门 | |||
| 洛谷 | [[B4449 密码强度]] | 入门 | |||
| 洛谷 | B4500 凯撒密码 | 入门 | ⭐⭐ | 字符串模拟 / 模运算 | 通过已破解的明文-密文对计算偏移量 delta = b[0]-a[0],对待解密串每个字符减去偏移量(转 0~25 数字域后模 26),再转回字母,时间复杂度 $O(\ |
| 洛谷 | B4556 字符转换 | 入门 | ⭐ | 字符串遍历 / 字符分类 | 遍历字符串,小写转大写(toupper)、大写转小写(tolower)、数字等非字母转 *,原地修改后输出,时间复杂度 \(O(n)\),空间复杂度 \(O(1)\) |
| 洛谷 | P1000 超级玛丽游戏 | 入门 | |||
| 洛谷 | P1200 你的飞碟在这儿 | 入门 | |||
| 洛谷 | P1321 单词覆盖还原 | 入门 | |||
| 洛谷 | P1597 语句解析 | 入门 | |||
| 洛谷 | P1765 手机 | 入门 | |||
| 洛谷 | P1914 小书童——凯撒密码 | 入门 | |||
| 洛谷 | [[P2955 Even Odd]] | 入门 | |||
| 洛谷 | P3741 小果的键盘 | 入门 | |||
| 洛谷 | P5015 标题统计 | 入门 | |||
| 洛谷 | P5733 自动修正 | 入门 | |||
| 洛谷 | [[P5832 Where Am I]] | 入门 | |||
| 洛谷 | P9946 Word Processor | 入门 | |||
| 洛谷 | P9973 你说得对,但是 AIGC | 入门 | |||
| 洛谷 | P11395 喵喵喵幼儿园 | 入门 | |||
| 洛谷 | AT_abc343_c 343 | 普及- | |||
| 洛谷 | AT_abc360_b Vertical Reading | 普及- | |||
| 洛谷 | AT_abc381_c 11 22 Substring | 普及- | |||
| 洛谷 | AT_abc385_b Santa Claus 1 | 普及- | |||
| 洛谷 | AT_abc386_c Operate 1 | 普及- | |||
| 洛谷 | AT_abc394_c Debug | 普及- | |||
| 洛谷 | AT_abc397_b Ticket Gate Log | 普及- | |||
| 洛谷 | [[B3843 密码合规]] | 普及- | |||
| 洛谷 | [[B3927 小杨的字典]] | 普及- | |||
| 洛谷 | [[B3958 相似字符串]] | 普及- | |||
| 洛谷 | B4039 回文拼接 | 普及- | |||
| 洛谷 | [[B4067 打印数字]] | 普及- | |||
| 洛谷 | B4262 词频统计 | 普及- | |||
| 洛谷 | B4396 字符匹配 | 普及- | |||
| 洛谷 | CF954B String Typing | 普及- | |||
| 洛谷 | P1055 ISBN号码 | 普及- | |||
| 洛谷 | P1071 潜伏者 | 普及- | |||
| 洛谷 | P1079 Vigenere 密码 | 普及- | |||
| 洛谷 | P1101 单词方阵 | 普及- | |||
| 洛谷 | [[P1203 坏掉的项链]] | 普及- | |||
| 洛谷 | P1308 统计单词数 | 普及- | |||
| 洛谷 | P1449 后缀表达式 | 普及- | |||
| 洛谷 | P1553 数字反转(升级版) | 普及- | |||
| 洛谷 | P1598 垂直柱状图 | 普及- | |||
| 洛谷 | P1603 斯诺登的密码 | 普及- | |||
| 洛谷 | P1781 宇宙总统 | 普及- | |||
| 洛谷 | P1957 口算练习题 | 普及- | |||
| 洛谷 | [[P2908 Word Power]] | 普及- | |||
| 洛谷 | [[P2957 Barn Echoes]] | 普及- | |||
| 洛谷 | P3612 Secret Cow Code | 普及- | |||
| 洛谷 | P5734 文字处理软件 | 普及- | |||
| 洛谷 | [[P5833 Livestock Lineup]] | 普及- | |||
| 洛谷 | [[P9012 Moo Operations]] | 普及- | |||
| 洛谷 | [[P10187 Palindrome Game]] | 普及- | |||
| 洛谷 | AT_abc338_d Island Tour | 普及 | |||
| 洛谷 | AT_abc411_d Conflict 2 | 普及 | |||
| 洛谷 | AT_abc414_c Palindromic in Both Bases | 普及 | |||
| 洛谷 | P1022 计算器的改良 | 普及 | |||
| 洛谷 | [[P1210 最长的回文]] | 普及 | |||
| 洛谷 | P1229 遍历问题 | 普及 | |||
| 洛谷 | P9013 Find and Replace | 提高+ | |||
| AtCoder | AT_awc0018_c Bulk Character Conversion | ||||
| AtCoder | AT_awc0019_b Cheerful Support Message | ||||
| AtCoder | AT_awc0033_a Grading the Answer Sheet | ||||
| AtCoder | AT_awc0082_c Train Rearrangement | ||||
| 学而思编程 | Z字形加密 | 普及奠基 | ⭐⭐ | 字符串 / 模拟 | 按 Z 字形将字符放入 \(k\) 行,方向在首尾行翻转,最后按行拼接输出密文。 |
| 学而思编程 | 疯狂的科学家 | 普及奠基 | ⭐ | 字符串 / 模拟 | 统计 \(A\) 与 \(B\) 不同的连续段数,每段可用一次操作翻转,答案即为不同段的数量。 |
| 学而思编程 | 解密 | 普及奠基 | ⭐ | 字符串 / 模拟 | 统计连续相同字符个数 \(k\),原始字符为加密字符减去 \((k-1)\) 的偏移(循环取模 \(26\))。 |
| 学而思编程 | 九宫格键盘 | 普及奠基 | ⭐ | 字符串 / 模拟 | 预存每个字母在九宫格上的按键次数,遍历字符串累加,空格计 \(1\) 次。 |
| 学而思编程 | 蚂蚁碰撞 | 普及奠基 | ⭐ | 字符串 / 双指针 | 去掉前缀 L 和后缀 R(它们不会碰撞),中间非 S 的字符个数即为碰撞次数。 |
| 学而思编程 | 特殊数字2 | 普及奠基 | ⭐⭐ | 字符串 / 模拟 | 用字符串存储大数,逐位取模判断能否被每个非零数码整除,避免直接对大数取模。 |
| 学而思编程 | 驼峰与蛇 | 普及奠基 | ⭐ | 字符串 / 模拟 | 遍历字符串,大写字母前加下划线转小写,下划线后字母转大写,其余直接输出。 |
| 学而思编程 | 优秀的排列 | 普及奠基 | ⭐ | 字符串 / 模拟 | 遍历字符串检查:无相邻 \(1\);每个 \(0\) 两边至少有一个 \(1\)(首尾特判)。 |
| 学而思编程 | 字符串 | 语言基础 | ⭐ | 字符串入门 / 桶计数 | 遍历字符串将大写字母统一转为小写(或小写转大写),用布尔数组 f[130] 按 ASCII 码标记出现过的字母,最后检查 'a' 到 'z' 是否全部标记为 true,若全部出现输出 Yes 否则输出 No,时间复杂度 $O(\ |
| 学而思编程 | 最小结果 | 语言基础 | ⭐⭐ | 字符串处理 / 枚举 | 先找到字符串中 '+' 的位置 mid,然后枚举左括号在 '+' 左侧的所有插入位置 \(i\)(\(0 \le i < mid\))和右括号在 '+' 右侧的所有插入位置 \(j\)(\(mid+1 \le j < n\)),将字符串分割为四段分别解析为整数 \(p, q, r, s\)(边界情况 \(p=1\) 或 \(s=1\)),计算 \(p \times (q+r) \times s\) 并取最小值,由于字符串长度很小,枚举复杂度可忽略,时间复杂度 \(O(n^2)\)(\(n\) 为字符串长度),空间复杂度 \(O(1)\)。 |
【字符串哈希】
算法模板
/*
核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果
*/
typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化哈希数组
s = " " + s;
void init()
{
p[0] = 1; // P^0 = 1
h[0] = 0; // 空串的哈希值为0
for (int i = 1; i <= n; i++)
{
p[i] = p[i - 1] * P; // 计算P^i
h[i] = h[i - 1] * P + s[i]; // 计算前缀哈希值
}
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1]; // 核心代码
}
// 判断两个子串是否相等
bool substr(int l1, int r1, int l2, int r2)
{
return get(l1, r1) == get(l2, r2);
}
算法应用
| 题目来源 | 标题 | 难度 | 星级 | 考察算法 | 一句话思路总结 |
|---|---|---|---|---|---|
| AcWing | 841 字符串哈希 | 简单 | ⭐⭐ | 字符串哈希(多项式滚动哈希) | 选定进制基数 \(P=131\),预处理前缀哈希数组 h[i] = h[i-1]*P + str[i] 和幂数组 p[i] = P^i(利用 unsigned long long 自然溢出等价于对 \(2^{64}\) 取模);子串 \([l,r]\) 的哈希值通过 get(l,r) = h[r] - h[l-1]*p[r-l+1] 在 \(O(1)\) 时间内计算;每次询问比较两个子串的哈希值是否相等,相等输出 Yes,否则输出 No;预处理 \(O(N)\),每次查询 \(O(1)\),总时间复杂度 \(O(N+M)\)。 |
| 洛谷 | P3370 字符串哈希 | 普及- | ⭐⭐ | 多项式哈希(自然溢出/大质数取模)+ 排序去重 | 将每个字符串按多项式哈希 \(H(s) = (H_{i-1} \cdot x + s[i]) \bmod y\)(或 unsigned long long 自然溢出)映射为整数指纹,对所有哈希值排序后用 unique 去重统计不同字符串数量,时间复杂度 \(O(N \cdot M + N \log N)\)。 |
| AtCoder | AT_awc0062_d Nearly Identical Signal Patterns |
【KMP】
算法模板
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
// 求模式串的Next数组:
ne[1] = 0;
for (int i=2, j=0; i<=m; i++)
{
while (j && p[i]!=p[j+1]) j = ne[j];
if (p[i]==p[j+1]) j++;
ne[i] = j;
}
// 匹配
for (int i=1, j=0; i<=n; i++)
{
while (j && s[i]!=p[j+1]) j = ne[j];
if (s[i]==p[j+1]) j++;
if (j==m)
{
printf("%d ", i-m+1);
}
}
算法应用
| 题目来源 | 标题 | 难度 | 星级 | 考察算法 | 一句话思路总结 |
|---|---|---|---|---|---|
| AcWing | 831 KMP字符串 | 简单 | ⭐⭐⭐ | KMP算法 | 预处理 next 数组记录模式串每个前缀的最长相等真前缀与真后缀长度,匹配时文本串指针不回退,失配时模式串指针按 next 回退;完全匹配后令 \(j = \text{next}[j]\) 继续寻找重叠出现,总时间复杂度 \(O(N+M)\)。 |
| 洛谷 | P1470 最长前缀 | 普及+ | |||
| 洛谷 | P3375 KMP | 普及+ | |||
| 洛谷 | P4391 无线传输 | 普及+ | |||
| 洛谷 | CF25E Test | 提高+ | |||
| AtCoder | AT_awc0045_b Order of Books on the Bookshelf | ⭐⭐ | KMP / 字符串匹配 | 将数组 \(B\) 视为模式串、数组 \(A\) 视为文本串,预处理 next 数组记录模式串的最长相等前后缀长度,失配时模式串指针 \(j\) 回退至 next[j] 而文本串指针 \(i\) 不回溯,在 \(O(N+M)\) 时间内找到 \(B\) 在 \(A\) 中首次出现的位置。 |
|
| AtCoder | AT_awc0098_c Highway Discount Pass |
【扩展KMP】
算法模板
void get_z(char *s)
{
z[1] = lb; // z[1]定义为模式串长度
for (int i = 2, l, r = 0; i <= lb; i++)
{
// 如果i在当前已知匹配区间内
if (i <= r)
{
z[i] = min(z[i - l + 1], r - i + 1);
}
// 扩展匹配
while (s[1 + z[i]] == s[i + z[i]])
{
z[i]++;
}
// 更新最右匹配边界
if (i + z[i] - 1 > r)
{
l = i;
r = i + z[i] - 1;
}
}
}
void get_p(char *s, char *t)
{
for (int i = 1, l, r = 0; i <= la; i++)
{
// 如果i在当前已知匹配区间内
if (i <= r)
{
p[i] = min(z[i - l + 1], r - i + 1);
}
// 扩展匹配
while (1 + p[i] <= lb && i + p[i] <= la && b[1 + p[i]] == s[i + p[i]])
{
p[i]++;
}
// 更新最右匹配边界
if (i + p[i] - 1 > r)
{
l = i;
r = i + p[i] - 1;
}
}
}
// 从下标1开始读取字符串
cin >> a + 1 >> b + 1;
// 计算字符串长度
la = strlen(a + 1);
lb = strlen(b + 1);
算法应用
| 题目来源 | 标题 | 难度 | 星级 | 考察算法 | 一句话思路总结 |
|---|---|---|---|---|---|
| 洛谷 | [[P5410 扩展KMP]] | 提高+ | |||
| AtCoder | AT_awc0047_e Repeat Playback and Partial Matching |
【字典树】
算法模板
int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串
void insert(char str[])
{
int p = 0;
for (int i=0; str[i]; i++)
{
int u = str[i]-'a';
// int u = getnum(str[i])
if (!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
// 查询字符串出现的次数
int query(char str[])
{
int p = 0;
for (int i=0; str[i]; i++)
{
int u = str[i] - 'a';
// int u = getnum(str[i])
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int getnum(char x){ // 获取输入字符对应的下标(如果输入的不只是小写字母的话)
if(x>='A'&&x<='Z')
return x-'A';
else if(x>='a'&&x<='z')
return x-'a'+26;
else
return x-'0'+52;
}
算法应用
| 题目来源 | 标题 | 难度 | 星级 | 考察算法 | 一句话思路总结 |
|---|---|---|---|---|---|
| AcWing | 143 最大异或对 | ||||
| AcWing | 835 Trie字符串统计 | 简单 | ⭐⭐ | Trie树(字典树) | 用 son[N][26] 数组构建 Trie 树,每个节点存储 26 个字母分支,插入时沿字符路径创建节点并在结尾 cnt[p]++ 记录出现次数,查询时沿路径遍历若中途断链则返回 0,否则返回 cnt[p]。 |
| AcWing | 937 单词查找树 | ||||
| 洛谷 | P1481 魔族密码 | 普及- | |||
| 洛谷 | P2580 于是他错误的点名开始了 | 普及 | |||
| 洛谷 | [[P8306 字典树]] | 普及 | |||
| 洛谷 | P4551 最长异或路径 | 普及+ | |||
| 洛谷 | P6824 可乐 | 普及+ | |||
| 洛谷 | P4735 最大异或和 | 省选 |
【AC自动机】
算法模板
// Trie树和AC自动机相关数据结构
int ch[N][26]; // Trie树的子节点数组,每个节点最多26个子节点(对应小写字母a-z)
int cnt[N]; // 记录每个节点是否是单词结尾及其出现次数
int idx; // Trie树的节点计数器
int ne[N]; // 失败指针(类似KMP中的next数组)
int n; // 模式串的数量
char str[N]; // 存储输入的字符串
// 将模式串插入Trie树
void insert(char *s) {
int p = 0; // 从根节点开始
for (int i = 0; s[i]; i++) {
int j = s[i] - 'a'; // 将字符转换为0-25的数字
if (!ch[p][j]) // 如果对应子节点不存在
ch[p][j] = ++idx; // 创建新节点
p = ch[p][j]; // 移动到子节点
}
cnt[p]++; // 标记该节点为单词结尾,并增加计数
}
// 构建AC自动机的失败指针
void build() {
queue<int> q; // 使用队列进行BFS
// 将根节点的直接子节点加入队列
for (int i = 0; i < 26; i++)
if (ch[0][i])
q.push(ch[0][i]);
while (q.size()) {
int u = q.front(); q.pop(); // 取出队首节点
for (int i = 0; i < 26; i++) {
int v = ch[u][i]; // 当前节点的子节点
if (v) {
// 如果子节点存在,设置其失败指针
ne[v] = ch[ne[u]][i];
q.push(v); // 将子节点加入队列
} else {
// 如果子节点不存在,直接指向失败指针的子节点
ch[u][i] = ch[ne[u]][i];
}
}
}
}
// 在目标字符串中查询模式串出现的总次数
int query(char *s) {
int ans = 0; // 记录匹配的模式串数量
for (int k = 0, i = 0; s[k]; k++) {
i = ch[i][s[k] - 'a']; // 根据当前字符转移到下一个状态
// 沿着失败指针检查所有可能的匹配
for (int j = i; j && ~cnt[j]; j = ne[j]) {
ans += cnt[j]; // 累加匹配到的模式串数量
cnt[j] = -1; // 标记已匹配,避免重复计数
}
}
return ans;
}
int main() {
cin >> n; // 输入模式串数量
for (int i = 1; i <= n; i++) {
cin >> str; // 输入模式串
insert(str); // 插入到Trie树中
}
build(); // 构建AC自动机的失败指针
cin >> str; // 输入目标字符串
cout << query(str) << endl; // 查询并输出匹配的模式串数量
return 0;
}
算法应用
| 题目来源 | 标题 | 难度 | 星级 | 考察算法 | 一句话思路总结 |
|---|---|---|---|---|---|
| AcWing | 1282 搜索关键词 | ||||
| 洛谷 | P3808 AC自动机(简单版) | 提高+ |
浙公网安备 33010602011771号