月月查华华的手机 【每日一题】4月2日题目精讲 枚举优化

https://ac.nowcoder.com/acm/problem/23053

 

这就是个简单的枚举优化,不要认为有多么的复杂高深(学算法最忌讳自己吓唬自己,你怕了就已经输了)。

我们可以观察,比如主串(华华的昵称)是abcdefgh子串(小姐姐的昵称)是 aez,那么当 a 匹配上了之后,再去枚举主串中的 bcd 其实是没有意义的,如果我们能做到在 a 之后跳到 a 后面第一个 e 然后在 e 之后跳到后面的第一个 z(如果有的话),直到子串遍历完或者跳不动了,就能出结果了。(如果主串中有两个一样的字母,我们肯定会选前一个,因为是子序列,如果选前一个不可以,那么选后面的同一个字母肯定就更不可以了,因为选越靠后的字母给后面留下的选择余地就越小。)

那么我们怎么实现在主串跳着枚举这个事情呢?

开一个数组next[i][′a′...′z′]表示主串第 i 个字母之后的第一个′a′...′z‘分别在哪一个位置,子串每次与之匹配的时候顺着往后走就行。

next数组的维护只需要从后往前扫描主串,在 扫描到第 iii 位的时候始终维护一个数组 last[′a′...′z′] 表示当前位置往后最靠前的字母′a′...′z′分别在哪,然后把这个数组复制给next[i]就可以了。

这样的话每次匹配的复杂度是O(∣Bi∣)的(只要顺着Bi遍历完就可以判断是不是子序列了)。总复杂度是O(26∣A∣+∑∣Bi∣)

自己按照题解的要求写的咋都过不了样例,心态崩了不管了。。。。

#include<bits/stdc++.h>
using namespace std;
const int N = 1000100;
char a[N], b[N];
int n;
int last[30];
int nxt[N][30];
void deal()
{
    memset(last, -1, sizeof(last));  //-1表示不存在
    int l = strlen(a);
    for (int i = l-1; i >= 0; i--)
    {
        for (int k = 0; k < 26; k++)
        {
             nxt[i][k] = last[k];   //将last['a'...'z']复制给当前的next[i]['a'...'z']
 
        }
        last[a[i] - 'a'] = i;     //当前字母修改last
 
    }
}
 
bool calc()
{
    int l1 = strlen(a);
    int l2 = strlen(b);
    int i = last[b[0] - 'a'];   //i初值直接赋为A中子串首字母的位置
    if (i == -1) return 0;      //如果A中没有这个首字母 直接无解
    for (int j = 1; j < l2; j++)  //j遍历子串中的字母(首字母就不必了)
    {
        i = nxt[i][b[j] - 'a'] ;   //i跳到前一个字母后面的当前字母
        if (i == -1) return 0;
    }
    return 1;
}
int main(){
    scanf("%s", a);
    deal();
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s",b);
        if (calc()) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

deebcjrb

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

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

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

打赏作者

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

抵扣说明:

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

余额充值