zoukankan      html  css  js  c++  java
  • 【每日一题】7.月月查华华的手机 (枚举 or 序列自动机)

    题目链接:Here

    题意总结:(N) 次查询串 (B) 是否是 (A) 的子序列。

    思路一

    个人做法,枚举原字符串的每一位,如果匹配当前字符串的字符则 m++ 直到字符串枚举完毕或者 m >= t.size()

    • (mathcal{O}(N))
    string s, t;
    void solve() {
        cin >> t;
        int m = 0;
        for (int i = 0; i < s.size(); ++i) {
            if (s[i] == t[m]) m++;
            if (m >= t.size()) break;
        }
        cout << (m >= t.size() ? "Yes" : "No") << "
    ";
    }
    int main() {
        ios_base::sync_with_stdio(false), cin.tie(0);
        int _;
        cin >> s;
        for (cin >> _; _--;) solve();
        return 0;
    }
    

    博客这么结束就太短了,AC之后看了下其他人的解法。发现还有一些好解法,如下介绍:

    思路二

    预处理 (A) 串每个位置后,每个字母首次出现的位置。
    (tp[i][j]) 表示A串第(i)个位置后,字符(j)首次出现的位置。从后往前扫一遍就出来了。

    时间复杂度:(mathcal{O}(26|A|))

    查询每个串B时用 (tp) 数组在A上跳。N次查询复杂度 (mathcal{O}(sum_{i = 1}^N|B|))

    这样做空间、时间复杂度是 O(2.6e7)。但是它过了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 7;
    char s[N];
    int tp[N][26];
    int main(void) {
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        for (int i = n - 1; i >= 0; --i) {
            for (int j = 0; j < 26; ++j) tp[i][j] = tp[i + 1][j];
            tp[i][s[i + 1] - 'a'] = i + 1;
        }
        int q;
        scanf("%d", &q);
        while (q--) {
            scanf("%s", s + 1);
            int m   = strlen(s + 1);
            int pos = 0, flag = 1;
            for (int i = 1; i <= m; ++i) {
                if (tp[pos][s[i] - 'a']) {
                    pos = tp[pos][s[i] - 'a'];
                } else {
                    flag = 0;
                    break;
                }
            }
            cout << (flag ? "Yes" : "No") << "
    ";
        }
        return 0;
    }
    

    思路三:序列自动机

    序列自动机的构建的核心在于 (next) 数组

    (next_{i,j}) 代表位置 (i) 后字符 (j) 第一次出现的位置。

    当从位置 (i + 1) 移动到 i 时,显然只有一个字符的 next 值发生了变化

    操作代码

    void Build(void) {
        n = strlen(s + 1);
        for (int i = n; i >= 1; i--) {
            for (int j = 0; j < 26; j++) fail[i - 1][j] = fail[i][j];
            fail[i - 1][s[i] - 'a'] = i;
        }
    }
    

    序列自动机的查找

    从头向后移动匹配,一旦失配,直接失败。

    bool check(void) {
        int m = strlen(t + 1), pos = 0;
        for(int i = 1; i <= m; i++) {
            pos = fail[pos][t[i] - 'a'];
            if(!pos) return false;
        }
        return true;
    }
    

    不难发现,这个题就是个模板题,粘贴好模板就行了。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxs = 1000000 + 7;
    int n, T;
    char s[maxs], t[maxs];
    int fail[maxs][28];
    void Build(void) {
        n = strlen(s + 1);
        for (int i = n; i >= 1; i--) {
            for (int j = 0; j < 26; j++) fail[i - 1][j] = fail[i][j];
            fail[i - 1][s[i] - 'a'] = i;
        }
    }
    bool check(void) {
        int m = strlen(t + 1), pos = 0;
        for (int i = 1; i <= m; i++) {
            pos = fail[pos][t[i] - 'a'];
            if (!pos) return false;
        }
        return true;
    }
    int main(void) {
        scanf("%s", s + 1);
        Build();
        scanf("%d", &T);
        while (T--) {
            scanf("%s", t + 1);
            cout << (check() ? "Yes" : "No") << "
    ";
        }
        return 0;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    对软件工程课程的期望
    自我介绍
    新目标
    课后作业-阅读任务-阅读笔记-4
    20150914-构建之法:现代软件工程-阅读笔记
    团队编程项目作业2-团队编程项目开发环境搭建过程
    团队编程项目作业2-团队编程项目设计文档
    个人-GIT使用方法
    结对编程项目作业2-结对编项目设计文档
    结对编程项目作业2-开发环境搭建过程
  • 原文地址:https://www.cnblogs.com/RioTian/p/14658846.html
Copyright © 2011-2022 走看看