题目:
给你a和b两个字符串,问b是不是a的子序列?
- 题解:
参考题解
注意看,子序列不是子串,两者含义不一样
一个字符串 s 被称作另一个字符串 S 的子串,表示 s 在 S中出现了。 一个字符串 s被称作另一个字符串 S 的子序列,说明从序列 S
通过去除某些元素但不破坏余下元素的相对位置(在前或在后)可得到序列 s 。
可以理解成:abc是abcd的子串,abc是adbewc的子序列,而非子串
求子串可以用kmp
那求子序列就用今天要讲的序列自动机
字符串A:abcdefgh
字符串B:aez
当a匹配过后,再去枚举后面的bcd没有意义,因为我们要找的是A中是否要aez三个字符,并且是这样的顺序,无须是连着的。如果我们能在a之后直接跳到a后面的一个e,再跳到e后面第一个z,等到子串B遍历完,或者在a中找不到了,我们就可以结束啦。
注意:我们在往后跳找字母时,比如a后面有很多个e,那么我们选第一个,因为如果第一个不行的话,那第二个第三个也白瞎,我们选的越往前其实选择空间就越大
仔细想想怎么能实现跳跃查找这个呢:
开个数组next[i][j]表示主串第i个字母之后的第一个‘a’+j的坐标,
next的维护只需要从后向前扫描主串,到第i位时维护一个数组last[j],j表示对应的‘a’~‘z’,last反映的是最靠前的字母j在哪里,然后把值赋给next就ok了
这样查找时顺着走就可以了
每次匹配B复杂度是O(|B|),
总复杂度O(26|A|+|B|),因为A要用26个字母都循环一遍所以是26|A|
可以通过下图理解
模板:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int n, t, next[maxn][30];
char s[maxn], str[maxn];
int main() {
scanf("%s", s+1);
int len = strlen(s+1);
for(int i=len; i; i--) {
for(int j=0; j<26; j++)
{
next[i-1][j] = next[i][j];
}
next[i-1][s[i]-'a'] = i;
}
scanf("%d", &t);
while(t--){
scanf("%s", str);
int lenc = strlen(str), f = 0;
int now=0;
for(int i=0; i<lenc; i++){
now = next[now][str[i]-'a'];
if(!now) {
f = 1;
break;
}
}
if(f) puts("No");
else puts("Yes");
}
}