• 字符串:序列自动机


    序列自动机:

    • 原理:用一个数组nxt[i][j]表示字符串从i下标开始的最近的 c = j + 'a' 这个字符出现的位置
    • 举个例子:
      • s = "abcca" (默认下标为1-5)
      • nxt[0][0] = 1,即字符串中出现的第一个字符'a'的位置为1
      • nxt[1][1] = 2,即字符串距离下标1之后的字符'b'出现的第一个位置为2
      • 以此类推我们可以得到 nxt[1][0] = 5,nxt[1][2] = 3 等等

    序列自动机的应用:

    1、快速查找一个字符串是否是另一个字符串的子序列。
    2、求解k个字符串的公共子序列个数。

    例题1:

    月月查华华的手机
    直接求出序列自动机,多次查询即可。

    点击查看折叠代码块
    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int maxn = 1e6+10;
    int n;
    char s[maxn];
    char tmp[maxn];
    int len;
    int nxt[maxn][26];
    
    void SeAM(){
        len = strlen(s+1);
        for (int i=len;i>=1;i--){
            for (int j=0;j<26;j++) nxt[i-1][j] = nxt[i][j];
            nxt[i-1][s[i]-'a'] = i;
        }
    }
    
    int main(){
        scanf("%s",s+1);
        SeAM();
        cin>>n;
        for (int i=1;i<=n;i++){
            scanf("%s",tmp+1);
            int l = strlen(tmp+1);
            int flag = 1;
            int pos = 0;
            for (int i=1;i<=l;i++){
                int p = nxt[pos][tmp[i]-'a'];
                if(!p){flag=0;break;}
                pos = p;
            }
            if(flag) puts("Yes");
            else puts("No");
        }
        return 0;
    }
    


    例题2:

    P3856 [TJOI2008]公共子串
    我们设 (dp[i][j][k]) 表示串a从i开始与串b从j开始与串c从k开始的公共子序列个数

    则转移方程为 (dp[i][j][k] += dp[newi][newj][newk]), 当且仅当 (nxt1[i][c]) && (nxt2[j][c]) && (nxt3[k][c]) 三个值同时不为0时成立, 0<=c<=25
    (newi = nxt1[i][c])
    (newj = nxt2[j][c])
    (newk = nxt3[k][c])

    特别的,如果 i && j && k,(dp[i][j][k]+=1)

    点击查看折叠代码块
    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int maxn = 1e3+10;
    int n;
    char s1[maxn],s2[maxn],s3[maxn];
    int nxt1[maxn][26],nxt2[maxn][26],nxt3[maxn][26];
    int l1,l2,l3;
    void init(){
        l1 = strlen(s1+1);
        for (int i=l1;i>=1;i--){
            for (int j=0;j<26;j++){
                nxt1[i-1][j] = nxt1[i][j];
            }
            nxt1[i-1][s1[i]-'a'] = i;
        }
    
        l2 = strlen(s2+1);
        for (int i=l2;i>=1;i--){
            for (int j=0;j<26;j++){
                nxt2[i-1][j] = nxt2[i][j];
            }
            nxt2[i-1][s2[i]-'a'] = i;
        }
    
        l3 = strlen(s3+1);
        for (int i=l3;i>=1;i--){
            for (int j=0;j<26;j++){
                nxt3[i-1][j] = nxt3[i][j];
            }
            nxt3[i-1][s3[i]-'a'] = i;
        }
    }
    
    ll dp[200][200][200];
    
    
    ll dfs(int p1,int p2,int p3){
        if(dp[p1][p2][p3]) return dp[p1][p2][p3];
        for (int i=0;i<26;i++){
            if(nxt1[p1][i] && nxt2[p2][i] && nxt3[p3][i]){
                dp[p1][p2][p3] += dfs(nxt1[p1][i],nxt2[p2][i],nxt3[p3][i]);
            }
        }
        if(p1 && p2 && p3) dp[p1][p2][p3]++;
        return dp[p1][p2][p3];
    }
    int main(){
        scanf("%s%s%s",s1+1,s2+1,s3+1);
        init();
        ll ans = dfs(0,0,0);
        cout<<ans;
        return 0;
    }
    


    例题3:


    我们固定左端点l,然后枚举c = 'a'-'z'每个字符在l后出现的第一个位置为p,则以l为左端点,p到n为右端点的所有的子串都对答案有1的贡献

    点击查看折叠代码块
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5+10;
    int n;
    char s[maxn];
    int nxt[maxn][26];
    
    void init(){
        for (int i=n;i>=1;i--){
            for (int j=0;j<26;j++) nxt[i-1][j] = nxt[i][j];
            nxt[i-1][s[i]-'a'] = i;
        }
    }
    int main(){
        cin>>(s+1);
        n = strlen(s+1);
        init();
        ll ans = 0;
        for (int i=1;i<=n;i++){
            for (int j=0;j<26;j++){
                int p = nxt[i-1][j];
                if(!p) continue;
                ans += (n-p+1);
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    
    你将不再是道具,而是成为人如其名的人
  • 相关阅读:
    nginx Server names
    ES6--变量的声明及解构赋值
    Android ListView and Tips.
    Eclipse自己定义keystore
    POJ 1129 Channel Allocation(DFS)
    机器学习笔记十三:Ensemble思想(上)
    设计模式——享元模式具体解释
    老猪带你玩转自定义控件三——sai大神带我实现ios 8 时间滚轮控件
    老猪带你玩转android自定义控件二——自定义索引栏listview
    android动手写控件系列——老猪叫你写相机
  • 原文地址:https://www.cnblogs.com/wsl-lld/p/14639052.html
Copyright © 2020-2023  润新知