序列自动机【模板】

序列自动机
算法分析

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

相当于建一棵树,根节点是一个空节点,它有26个孩子,表示每一个字母最早出现的位置

举个例子:

s = “abcca” (默认数组下标为1-5) (a–z==0–25) ne[0][0] = 1。即字符串中出现的第一个字符’a’的位置为1。
ne[1][1] = 2,即字符串距离下标1之后的字符’b’出现的第一个位置为2
以此类推我们可以得到 ne[1][0] = 5(距离字符串下标1的首a位置),ne[1][2] = 3 (距离字符串下标1的首c位置)等等

同样第一个字符也有这样的26个孩子,这样从根节点到任意一个叶子节点都是原串的一个子序列,这样判断一个字符串t是不是原串的子序列只要将t中的每一个字符在那棵树里跑一下,如果存在这样的路径就表示t是s的一个子序列。

那么怎么建树呢?

如果正着建树的话每次都要找到后面最早出现的字符的位置,操作过于复杂,所以我们倒着建树,用一个 tree[30]数组表示遍历到第i个字符时后面这26个字符从后往前看最晚出现的位置,也就是第i个字符后面的26个字符最在出现的位置,用它来更新 然后再将这个字符在 tree数组中的位置更新为当前的位置tree[s[i]−′a′]=i

构建序列自动机

ne[i][j]存的是在字符串 s 中第 i 位后面第一个 字母出现的位置。(0<j<26)

void build()
{ 
 memset(tree,-1,sizeof(tree));//初始化未出现的字符
	//处理每一个字符
    for(int i=s.length();i>=0;i--)
    {
//找出第i个字符后面的26个字母最早出现的字符的位置
    for(int j=0;j<26;j++)
    ne[i][j]=tree[j];
 //用当前字符更新当前字符在原串中从后向前最晚出现的位置
        tree[s[i]-'a']=i;
    }
}

子序列查询

查询字符串 t 是否是上述 s 的子序列

bool check()
{
   int start=tree[t[0]-'a'];//start为t第一个字符位置
    if(start==-1)
    return false;//第一个字符不在s中
    for(int i=1;i<t.length();i++)
    {
    start=ne[start][t[i]-'a'];//位置移动的下一个字符的位置
       if(start==-1)
       return false;
    }
    return true;
}

模板题:月月查华华的手机
在这里插入图片描述
输入

noiauwfaurainairtqltqlmomomo
8
rain
air
tql
ntt
xiaobai
oiiiooo
orzcnzcnznb
ooooo

输出

Yes
Yes
Yes
Yes
No
Yes
No
No

在这里插入图片描述
AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
string s,t;
int ne[N][30];
int tree[30];//在字符串最前面出现的位置
void build()
{ memset(tree,-1,sizeof(tree));//初始化未出现的字符
	//处理每一个字符
    for(int i=s.length();i>=0;i--)
    {
//找出第i个字符后面的26个字母最早出现的字符的位置
    for(int j=0;j<26;j++)
    ne[i][j]=tree[j];
 //用当前字符更新当前字符在原串中从后向前最晚出现的位置
        tree[s[i]-'a']=i;
    }
}
bool check()
{
   int start=tree[t[0]-'a'];//start为t第一个字符位置
    if(start==-1)
    return false;//第一个字符不在s中
    for(int i=1;i<t.length();i++)
    {
    start=ne[start][t[i]-'a'];//位置移动的下一个字符的位置
       if(start==-1)
       return false;
    }
    return true;
}
int main()
{
    int n;
   getline(cin,s);
    build();
    cin>>n;
    while(n--)
    {
        cin>>t;
        if(check())
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

稚皓君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值