子序列自动机

子序列:从原字符串中删除任意个(可以为零、可以为全部)字符得到的新字符串。
https://ac.nowcoder.com/acm/contest/20960/1023
对于给定的仅由小写字母构成的字符串 s s s,判断其他 n n n 个字符串是不是 s s s 的子序列。

#include<bits/stdc++.h>

using namespace std;
using ll = long long;

const int maxn = 1e6+3;
int last[30],nxt[maxn][30];
// last[i] 表示某个字母最后出现的位置
// nxt[i][j] 表示股 i 位置后某个字母最先出现的位置

void solve()
{
    string s; cin>>s;
    memset(last,-1,sizeof(last));
    for(int i=s.length()-1;i>=0;i--)
    {
        for(int j=0;j<26;++j)
        {
            nxt[i][j]=last[j];
        }
        last[s[i]-'a']=i;
    }
    int n; cin>>n;
    string t;
    while(n--)
    {
        cin>>t;
        int pos=last[t[0]-'a']; // 先定位到第一个字母所在的位置
        if(pos==-1) {cout<<"No"<<endl; continue;}
        bool flag=1;
        for(int i=1;i<t.length();i++)
        {
            pos=nxt[pos][t[i]-'a'];
            if(pos==-1) 
            {
                flag=0;
                break;
            }
        }
        if(flag) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T; T=1;
    while(T--)
    solve();
}
<think>嗯,用户现在问的是子串和子序列的区别。之前他问了关于C++中vector初始化的方法,现在转向字符串相关的概念,可能是在学习算法或者数据结构,尤其是在处理字符串问题时遇到了这两个术语。需要确认用户是否有编程背景,特别是字符串处理方面的知识。 首先,得明确子串和子序列的定义。子串是字符串中连续字符组成的序列,而子序列是不需要连续但保持相对顺序的字符序列。比如,对于字符串"abc",子串有"a", "b", "c", "ab", "bc", "abc",而子序列则包括所有子串以及像"ac"这样的组合。 用户可能是在准备面试题,比如常见的动态规划问题如最长公共子串和最长公共子序列。这时候区分这两个概念非常重要,因为解决方法不同。另外,用户可能在使用一些字符串处理函数时遇到了困惑,例如在Python中字符串切片得到的子串,而生成子序列可能需要递归或迭代的方法。 需要检查是否存在常见的误解,比如是否有人认为子序列必须保持相邻,或者子串可以有间隔。这时候用例子来对比会更清晰。同时,应该指出它们在算法中的应用场景,比如子串常用于搜索和匹配,而子序列用于更复杂的模式识别。 另外,用户可能想知道如何在代码中生成子串和子序列。例如,生成所有子串可以通过双重循环遍历起始和结束位置,而生成子序列可能需要回溯或位操作。这时候给出具体的代码示例会有帮助,但用户的问题可能更偏向理论,所以需要先解释清楚概念,再考虑是否提供代码。 还要注意术语的正确性,确保没有混淆子数组和子串,子集和子序列的区别。特别是在不同编程语言中,可能有不同的术语习惯,但用户的问题比较基础,应该用通用定义来解释。 最后,总结两者的区别,强调连续性和顺序的重要性,以及它们在不同算法问题中的应用。这样用户能够明确区分,并在实际问题中正确应用。</think>子串(Substring)和子序列(Subsequence)是字符串处理中的两个核心概念,它们的区别主要体现在**连续性要求**和**应用场景**上。以下是详细对比: --- ### **定义对比** | | 子串 (Substring) | 子序列 (Subsequence) | |----------|-------------------------------|--------------------------------| | **连续性** | **必须连续** | **可以不连续**(但必须保持原始顺序)| | **示例** | "abc" 的子串:"a", "b", "ab" | "abc" 的子序列:"a", "ac", "bc"| | **数学表示** | $s[i..j]$ $(0 \leq i \leq j < n)$ | 索引序列 $i_1 < i_2 < ... < i_k$ 的字符组合 | --- ### **关键差异** #### 1. 连续性要求 - **子串**:必须选取原始字符串中**连续的字符片段** $\text{例:对于 "apple"}$ ✅ 合法子串:"app", "ple" ❌ 非法子串:"ape"(p和e不连续) - **子序列**:只需保持**相对顺序**,不要求连续 $\text{例:对于 "algorithm"}$ ✅ 合法子序列:"agm", "lth" ❌ 非法子序列:"mga"(顺序颠倒) #### 2. 数量级差异 对于长度为$n$的字符串: - **子串数量**:$\frac{n(n+1)}{2}$(多项式级别) $\text{例如:n=3 → 6个子串}$ - **子序列数量**:$2^n - 1$(指数级别) $\text{例如:n=3 → 7个子序列}$ #### 3. 常见算法应用 | 子串相关算法 | 子序列相关算法 | |------------------------------|----------------------------------| | KMP字符串匹配 | 最长公共子序列(LCS) | | 滑动窗口求最长无重复子串 | 编辑距离计算 | | 后缀自动机 | 判断子序列存在性(双指针法) | --- ### **可视化理解** ```text 原始字符串: A B C D E 子串示例 → A B C └─┬─┘(连续) 子序列示例 → A C E │ │ │(保持顺序但不连续) ``` --- ### **代码示例对比** #### 生成所有子串(Python) ```python s = "abc" n = len(s) substrings = [s[i:j+1] for i in range(n) for j in range(i, n)] # 输出:['a', 'ab', 'abc', 'b', 'bc', 'c'] ``` #### 生成所有子序列(Python) ```python def subseq(s): if not s: return [""] rest = subseq(s[1:]) return rest + [s[0] + x for x in rest] print(subseq("abc"))[1:] # 排除空字符串 # 输出:['c', 'b', 'bc', 'a', 'ac', 'ab', 'abc'] ``` --- ### **经典问题对比** | 问题类型 | 子串问题示例 | 子序列问题示例 | |----------------------|-------------------------------|-------------------------------| | 最长匹配问题 | 最长无重复字符子串 | 最长递增子序列(LIS) | | 公共性问题 | 最长公共子串 | 最长公共子序列(LCS) | | 存在性判断 | 判断是否为子串(直接in操作) | 判断子序列存在性(双指针法) | --- ### **常见误解澄清** 1. **子序列 ≠ 子集**:子序列必须保持原始顺序 $\text{例:对于 "abc"}$ ✅ 子序列:"ac" ❌ 子集(不考虑顺序):"ca" 2. **空字符串的特殊性**: - 空字符串既是所有字符串的子串,也是子序列 3. **单字符情况**: - 单个字符既是子串,也是子序列 --- 掌握这一区别对解决**动态规划问题**(如LCS问题)和**字符串匹配算法**(如KMP)至关重要。在算法面试中,明确区分这两个概念可以避免因理解偏差导致的错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值