题目:https://ac.nowcoder.com/acm/problem/23053

问题本质:判断一个序列是不是另一个序列的子序列。

基本解法:定义两个指针i、j分别指向s串和t串,遍历s串如果$s[i]=t[j]$,则j++。这样的做法最坏的情况会遍历整个s串,虽然对于单次查询复杂度可以接受,但对于本题的多次查询显然不行。

贪心:如果在s串中有多个$s[i]=t[0]$,那么肯定选用i值小的,因为i值小的可拓展性更好,或者说i值小的无法找到串t,则i值大的也无法找到。

问题关键:假设已经找到$s[i]=t[j]$,如何快速的找到$s[k]=t[j+1],i<k$?

预处理:从后往前遍历串s,并用数组$first[]$记住每个字符最早出现的位置,并把$first[]$的值赋给$to[i][]$,这样找到$s[i]=t[j]$时可以直接跳到$s[k]=t[j+1],k=to[i][t[j+1]]$。这个预处理是优化的核心部分,如果不能理解的话建议造几个简单的样例手动推一下。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;     ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf;   ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 2e5 + 117;

int n;
int to[MAXN][30], first[30];
char s[MAXN], t[MAXN];
bool solve() {
    int len = strlen(t);
    int id = first[t[0] - 'a'];
    if(id == -1) return false;
    for(int i = 1; i < len; i++) {
        id = to[id][t[i] - 'a'];
        if(id == -1) return false;
    }
    return true;
}
int main() {
    scanf("%s", s);
    scanf("%d", &n);
    memset(first, -1, sizeof(first));
    for(int i = strlen(s) - 1; i >= 0; i--) {
        for(int j = 0; j < 30; j++) to[i][j] = first[j];
        first[s[i] - 'a'] = i;
    }
    while(n--) {
        scanf("%s", t);
        if(solve()) puts("Yes");
        else puts("No");
    }
    return 0;
}
Last modification:April 4th, 2020 at 11:34 pm
如果觉得我的文章对你有用,请随意赞赏