zoukankan      html  css  js  c++  java
  • 序列自动机

    https://www.cnblogs.com/31415926535x/p/10504504.html

    昨天在牛客碰到了这样的一道题,判断一些字符串是不是原串的子序列,,,因为之前做过一些LCS子序列的题,,,就想,这不贼简单,,用lcs求一下每个子串和原串,,然后判断LCS的长度是不是等于要判断的那个串的长度,,,然后,,T了,,,

    因为dp求LCS几个串还好说,,但是当串又多又长时,,,不仅会T,,dp数组不弄滚动数组还会MLE,,,

    之后看了题解了解到这个处理子序列的好东西,序列自动机,,,

    分析

    序列自动机实质还是用空间换时间,,它有一个数组 (nxt[i][j](nxt[maxn][26]),,表示原串s的第i位后面那26个字符j出现的最早的 位置,,

    相当于建了一棵树,,根节点是一个空节点,,,它有26个孩子,,表示每一个字母最早出现的位置,,,那么原串的第一个字符 (s[0]) 就使得 (nxt[0][s[0] - 'a'] = 1),,第二个字符就是 (nxt[0][s[1]-'a']=2),,,等等等等,,,同样第一个字符也有这样的26个孩子,,,这样从根节点到任意一个叶子节点都是原串的一个子序列,,

    这样判断一个字符串t是不是原串的子序列只要将t中的每一个字符在那棵树里跑一下,,,如果存在这样的路径就表示t是s的一个子序列,,,

    那么怎么建树呢,,

    如果正着建树的话每次都要找到后面最早出现的字符的位置,,,不太好弄,,所以我们倒着建树,,用一个 (now[26]) 数组表示遍历到第i个字符时后面这26个字符从后往前看最晚出现的位置,,也就是第i个字符后面的26个字符最在出现的位置,,,用它来更新 (nxt[i][1 o 26]),,然后再将这个字符在 (now) 数组中的位置更新为当前的位置,,(now[s[i]-'a']=i),,,

    最后的实现就是这样子:

    int nxt[maxn][30];
    int now[30];
    char s[maxn];
    void init()
    {
        //序列自动机预处理
        memset(now, -1, sizeof now);            //mow_i表示第i个字母在原串中从后向前最晚出现的位置
        int len = strlen(s);
        --len;
        for(int i = len; ~i; --i)               //处理每一个字符
        {
            for(int j = 0; j < 26; ++j)        //找出第i个字符后面的26个字母最早出现的字符的位置
                nxt[i][j] = now[j];
            now[s[i] - 'a'] = i;                //用当前字符更新当前字符在原串中从后向前最晚出现的位置
        }
    }
    

    例题

    牛客392-j

    题意

    题意就是判断n个字符串是不是原串的子序列,,,

    代码

    #include <bits/stdc++.h>
    #define aaa cout<<233<<endl;
    #define endl '
    '
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int inf = 0x3f3f3f3f;//1061109567
    const ll linf = 0x3f3f3f3f3f3f3f;
    const double eps = 1e-6;
    const double pi = 3.14159265358979;
    const int maxn = 1e6 + 5;
    const int maxm = 1e5 + 5;
    const int mod = 1e9 + 7;
    inline int read() {
        char c = getchar(); int x = 0, f = 1;
        while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int nxt[maxn][30];
    int now[30];
    char s[maxn];
    void init()
    {
        //序列自动机预处理
        memset(now, -1, sizeof now);            //mow_i表示第i个字母在原串中从后向前最晚出现的位置
        int len = strlen(s);
        --len;
        for(int i = len; ~i; --i)               //处理每一个字符
        {
            for(int j = 0; j < 26; ++j)        //找出第i个字符后面的26个字母最早出现的字符的位置
                nxt[i][j] = now[j];
            now[s[i] - 'a'] = i;                //用当前字符更新当前字符在原串中从后向前最晚出现的位置
        }
    }
    char ss[maxn];
    int main()
    {
    //    freopen("233.in" , "r" , stdin);
    //    freopen("233.out" , "w" , stdout);
    //    ios_base::sync_with_stdio(0);
    //    cin.tie(0);cout.tie(0);
        scanf("%s", s);
        int n; scanf("%d", &n);
        init();
        while(n--)
        {
            scanf("%s", ss);
            int loc = now[ss[0] - 'a'];             //没有以子串第一个字符出现的子序列时
            if(!~loc)printf("No
    ");
            else
            {
                bool flag = true;
                int len = strlen(ss);
                for(int i = 1; i < len; ++i)
                {
                    loc = nxt[loc][ss[i] - 'a'];    //寻找母串中子串第i个字符下一次出现的位置
                    if(!~loc)                       //没有就退出
                    {
                        flag = false;
                        break;
                    }
                }
                if(flag)printf("Yes
    ");
                else    printf("No
    ");
            }
        }
        return 0;
    }
    

    牛客156-d

    题意

    找出所有(abcdefghi)的排列是原串的子序列的个数,,,

    判断条件改一下就行了,,

    代码

    #include <bits/stdc++.h>
    #define aaa cout<<233<<endl;
    #define endl '
    '
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int inf = 0x3f3f3f3f;//1061109567
    const ll linf = 0x3f3f3f3f3f3f3f;
    const double eps = 1e-6;
    const double pi = 3.14159265358979;
    const int maxn = 1e6 + 5;
    const int maxm = 1e5 + 5;
    const int mod = 1e9 + 7;
    inline int read() {
        char c = getchar(); int x = 0, f = 1;
        while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int now[10];
    int nxt[maxn][10];
    char s[maxn];
    void init()
    {
        memset(now, -1, sizeof now);
        int len = strlen(s);
        --len;
        for(int i = len; ~i; --i)
        {
            for(int j = 0; j < 9; ++j)
                nxt[i][j] = now[j];
            now[s[i] - 'a'] = i;
        }
    }
    int main()
    {
    //    freopen("233.in" , "r" , stdin);
    //    freopen("233.out" , "w" , stdout);
    //    ios_base::sync_with_stdio(0);
    //    cin.tie(0);cout.tie(0);
        scanf("%s", s);
        int ans = 0;
        char a[] = "abcdefghi";
        init();
        do
        {
            int loc = now[a[0] - 'a'];
            if(!~loc)continue;
            for(int i = 1; i < 9; ++i)
            {
                loc = nxt[loc][a[i] - 'a'];
                if(!~loc)break;
            }
            if(s[loc] == a[8])++ans;
        }while(next_permutation(a, a + 9));
        printf("%d", ans);
        return 0;
    }
    

    还有一些序列自动机加dp什么的,,,以后再看把,,,

    这里有很多知识点

    (end)

  • 相关阅读:
    Django使用xadmin集成富文本编辑器Ueditor(方法二)
    Django-xadmin后台配置富文本编辑器(方法一)
    求解函数不等式[给定具体函数]
    区间断想
    函数方程和函数不等式
    对函数的再理解
    多题一解
    破解函数性质中的表达难点
    [数学模型]应用举例
    函数的迭代
  • 原文地址:https://www.cnblogs.com/31415926535x/p/10504504.html
Copyright © 2011-2022 走看看