常用算法代码模板与题单 | 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\) 统计字符 EW 的出现次数,分别代表东军和西军士兵人数,直接比较两个计数器大小输出 EastWest,由于字符串长度为奇数保证结果必不相等,时间复杂度 $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自动机(简单版) 提高+
posted @ 2026-06-27 14:23  团爸讲算法  阅读(25)  评论(0)    收藏  举报