序列自动机:判断子序列

序列自动机:判断子序列

序列自动机

  设想这样一个问题:判断字符串 s 是否为字符串 t 的子序列(注意子序列和子串不同,子串要求连续,子序列不要求连续),我们可以使用双指针,两个指针分别从s和t的第一个元素开始,不断进行匹配移动,时间复杂度为O(n+m),n,m分别为s和t的长度。但若有大量输入的 S,称作 S1, S2, … , Sk 其中k非常大,例如k >= 10亿,你需要依次检查它们是否为 T 的子序列,若我们仍然使用双指针进行逐个判断,时间复杂度为O(k * m + ∑n),这样的时间复杂度是我们无法接受的,这时序列自动机就该登场了,序列自动机使用空间换时间的思想,能将上述时间复杂度优化到O(m + ∑n),把非常大的参数k消去了。
  序列自动机是一个可以快速判断字符串s是否是字符串t的子序列的一个方法(在存在非常多的s需要判断的情况下)。
  序列自动机使用了一个二维数组next[i] [j],next[i] [j]表示从第i个位置起,字符j出现的第一个位置。
  以判断子序列为例(设序列全由小写字母组成),我们可以创建一个二维数组Next[N][26],第一维的N指序列长度,第二维指26个字母。那么Next[a][b]里面存的是什么信息呢?存的是从字符串的第a位开始距离第a
位最近的字符b的下标(即从第a个位置起,字符b出现的第一个位置)。结合图例来理解:
在这里插入图片描述
  Next[1] [3]表示从第1个位置起,字符d出现的第一个位置(3就是表示d,26个字母表示为数字0 ~ 25),在上图中Next[1] [3] = 4。Next[5] [1]同理,表示从第5个位置起,字符b出现的第一个位置(距离第5位最近的字符b的下标),为7。

  我们如何使用上述Next数组判断字符串s是否为t的子序列呢?举个例子,设t=acbdacbad,s=abcd,那么求s是否为t的子序列可以拆分为4个小问题:
  (1)t串中距离下标1最近的’a’在哪?答:Next[1][0]=1。
  (2)t串中距离下标Next[1][0]+1=2最近的’b’在哪?答:Next[2][1]=3。
  (3)t串中距离下标Next[2][1]+1=4最近的’c’在哪?答:Next[4][2]=6。
  (4)t串中距离下标Next[4][2]+1=7最近的’d’在哪?答:Next[7][3]=9。
在这里插入图片描述
  从下标1开始,我们利用Next数组逐个查询s中每个字符出现的位置,每查到一个字符的位置,我们将该位置的下一位作为新起点,继续利用Next数组查询,若s中每个字符的位置都能查到,则s为子序列,否则不是子序列。
  设Next[i] [j] = 0表示从位置i开始,字符j不存在,则利用Next数组判断s是否为子序列的算法模板如下:

    static boolean check(String s) {
   
        int i, pos = 1;
        for (i = 0;i < s.length();i++) {
   
            pos 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

happy19991001

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

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

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

打赏作者

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

抵扣说明:

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

余额充值