2016集训测试赛(二十四)Problem B: Prz

Description

Solution

这道题有两个关键点:

  • 如何找到以原串某一个位置为结尾的某个子序列的最晚出现位置
  • 如何找到原串中某个位置之前的所有数字的最晚出现位置中的最大值

第一个关键点: 我们注意到每个数字在\(M\)\(L\)中最多只会出现一次. 以\(M\)为例, 我们从前往后逐位在原串中匹配, 数组f[i]表示\(M\)的前\(i\)位在原串中当前位置之前的最晚出现位置. 假设当前数字\(x\)\(M\)中出现位置为\(p\), 则
\[ f[p] = \begin{cases} f[p] = i, \ p == 1 \\ f[p] = f[p - 1], \ p > 1 \end{cases} \]
至于其他长度的子序列, 其最晚出现位置并不会发生变化.

第二个关键点: 我们记录每个数字的最晚出现位置即可.

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>

using namespace std;
namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1; char c;
        while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
        while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
}
const int N = (int)1e6, M = (int)1e6;
int main()
{

#ifndef ONLINE_JUDGE

    freopen("prz.in", "r", stdin);
    freopen("prz.out", "w", stdout);

#endif

    using namespace Zeonfai;
    int len = getInt(), m = getInt();
    static int a[N + 1], s[N + 1], t[N + 1];
    for(int i = 1; i <= len; ++ i) a[i] = getInt();
    int lenS = getInt(), lenT = getInt();
    for(int i = 1; i <= lenS; ++ i) s[i] = getInt();
    for(int i = 1; i <= lenT; ++ i) t[i] = getInt();
    static int mp[N + 1]; memset(mp, -1, sizeof(mp));
    for(int i = 1; i <= lenS; ++ i) mp[s[i]] = i;
    static int rec[N + 1]; memset(rec, -1, sizeof(rec));
    static int f[N + 1], g[N + 1];
    for(int i = 1; i <= len; ++ i)
    {
        if(~ mp[a[i]])
        {
            if(mp[a[i]] == 1) rec[mp[a[i]]] = i;
            else rec[mp[a[i]]] = rec[mp[a[i]] - 1];
        }
        f[i] = rec[lenS];
    }
    memset(mp, -1, sizeof(mp));
    for(int i = 1; i <= lenT; ++ i) mp[t[i]] = i;
    memset(rec, -1, sizeof(rec));
    for(int i = len; i; -- i)
    {
        if(~ mp[a[i]])
        {
            if(mp[a[i]] == 1) rec[mp[a[i]]] = i;
            else rec[mp[a[i]]] = rec[mp[a[i]] - 1];
        }
        g[i] = rec[lenT];
    }
    memset(mp, -1, sizeof(mp));
    for(int i = len; i; -- i) if(mp[a[i]] == -1) mp[a[i]] = i;
    static int lst[N + 1]; lst[0] = -1;
    for(int i = 1; i <= len; ++ i) lst[i] = max(mp[a[i]], lst[i - 1]);
    int cnt = 0; static int ans[N];
    for(int i = 1; i <= len; ++ i) if(~ f[i] && ~ g[i] && a[i] == s[lenS] && lst[f[i] - 1] > g[i]) ans[cnt ++] = i;
    printf("%d\n", cnt);
    for(int i = 0; i < cnt; ++ i) printf("%d ", ans[i]);
}

转载于:https://www.cnblogs.com/ZeonfaiHo/p/7519029.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值