题目
子序列判断,双指针
思路
将题目翻译一下:
给你一个字符串 s s s,然后给定 m m m 个字符串的询问,每次询问判断给定的字符串 t t t 是否是 s s s 的子序列。
判断子序列的话,按理来说应该比判断子串更简单(在不使用库函数的情况下),因为判断子串需要用到 KMP 算法来优化才能过。那么判断子序列为什么简单呢?
因为判断子序列可以用时间复杂度为 O ( n + m ) O(n+m) O(n+m) 的天然算法解决,而子串不行(子串的天然算法是 O ( n m ) O(nm) O(nm) 的暴力算法。
那判断子序列的天然算法是什么?
双指针,这里给出伪代码,还是很容易理解的:
// 假设主串是 s, 子串是 t
slen = s.length
tlen = t.length
i = 0, j = 0
while i < slen && j < tlen:
while i < slen && s[i] != t[j]:
i = i + 1
if i == slen:
// 找完了也没找到,不是子序列
report t is not s's subsequence
else:
j = j + 1
i = i + 1 // 思考一下为什么 i 也要加1
代码
#include <stdio.h>
#define N 1000005
/**
* @brief 求字符串 name 是否是 s 的子序列
*
* @param s 主串
* @param name 子串
* @return int 返回 0 代表不是,否则代表是
*/
int is_subseq(const char* s, const char* name) {
int i = 0, j = 0;
while (s[i] && name[j]) {
while (s[i] && s[i] != name[j]) i++;
if (!s[i]) return 0;
j++;
i++;
}
return !name[j];
}
int main(void) {
// 华华的昵称
char s[N];
// 推荐好友个数
int n;
// 每个好友的昵称
char name[N];
scanf("%s%d", s, &n);
while (n--) {
scanf("%s", name);
printf("%s\n", is_subseq(s, name) ? "Yes" : "No");
}
return 0;
}
其他
简单地做一下复杂度的分析,由于有 m m m 组询问,所以时间复杂度一定要乘以一个 m m m,每次询问花费的时间复杂度为两个字符串的长度 O ( n + k ) O(n+k) O(n+k),所以最终的时间复杂度为 O ( m ( n + k ) ) O(m(n+k)) O(m(n+k)),可以近似地看作是 O ( m n ) O(mn) O(mn) 的。
也有预处理的算法,即先预处理主串的字符成一个表, m m m 次询问就只需要查表即可,不过该算法也需要 O ( m k ) O(mk) O(mk) 的时间复杂度,与本文记录的算法相比优化不是很大,这里不做记录。
当然还有其他更高级(
复杂)的算法。