Solution
这道题有两个关键点:
- 如何找到以原串某一个位置为结尾的某个子序列的最晚出现位置
- 如何找到原串中某个位置之前的所有数字的最晚出现位置中的最大值
第一个关键点: 我们注意到每个数字在(M)和(L)中最多只会出现一次. 以(M)为例, 我们从前往后逐位在原串中匹配, 数组f[i]
表示(M)的前(i)位在原串中当前位置之前的最晚出现位置. 假设当前数字(x)在(M)中出现位置为(p), 则
[f[p] = egin{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
", cnt);
for(int i = 0; i < cnt; ++ i) printf("%d ", ans[i]);
}