题目大意:
给定n个人排成的一个队列,每个人身高为x[i](取值为1-k)。再给一个长度为m的模式串,问在原队列中最多能取出多少个匹配模式串的子队列。
其中匹配是指两个串分别离散之后完全一样,即对任意i、j,若在模式串a中a[i]<a[j],那么在原串b中也要有b[i]<b[j],a[i]>a[j]、a[i]==a[j]同理。
k(1<=k<=25),n (1<=n<=10^5) ,m(1<=m<=n)
解法:
基础的想法是枚举出所有的匹配串,然后做区间调度的贪心出答案。这样就要解决2个问题。
第一个是匹配的方式问题,怎么判断是否匹配。
第二个是时间问题,m可能很大,所以匹配时候O(m)是不现实的,必须把匹配用时间尽量缩短。
对于第一个问题我是这么解决的:用k个vector(记为num)记录模式串a(下面简称为a)中每一个数字出现的位置,num[i][j]就是模式串中数字i第j次出现的位置,这个可以在O(m)的时间预处理出来。
这时满足匹配的条件是:对于模式串中的每个数字i,记这个数字在当前串中的对应数为Hash[num[i][0]]。那么对所有的i都要有Hash[num[i][0]]>Hash[num[j][0]](j是模式串中出现的比i小的上一个数字),并且对所有的k,都有Hash[num[i][k]] == Hash[num[i][0]]。
那么事情就简单了,把num给预处理好之后,先判断Hash是否满足条件,这个最多做25次比较。此时如果Hash不满足条件,则表示不匹配继续往下走;而如果满足条件,则对每个数i判断是否Hash[num[i][k]] == Hash[num[i][0]],这中间如果发现不满足则跳出。那么匹配的问题就解决了。
至于第二个问题,可以把贪心的思想加入到匹配的过程中来。区间调度的贪心思路是每次取可选的中结束最早的区间,而因为这题的区间长度都是m,结束最早的区间就是最早出现的匹配区间。也就是说,我匹配到一个区间之后就选这个区间,并且不再会选与这个区间相交的区间,从这个区间结束点开始继续匹配就好,也就是跳跃性进行匹配。
这样可以达到的效果是1.如果是不匹配区间我可以很快跳出。2.如果是匹配区间我会一次往后跳跃m个位置。这样复杂度大概在O(n+若干个m),不是很好算,但总体来说会比较快。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 100000+20;
int n,m,k;
int x[maxn];
int a[maxn];
int s[maxn],t[maxn];
vector<int> num[30];
int cnt[maxn][30];
int mo[30];
int Hash[30];
int main()
{
while(~scanf("%d%d%d",&n,&m,&k)){
memset(cnt,0,sizeof(cnt));
for(int i=0;i<=k;i++){
num[i].clear();
mo[i]=0;
}
for(int i=0;i<n;i++){
scanf("%d",&x[i]);
}
for(int i=0;i<m;i++){
scanf("%d",&a[i]);
mo[a[i]]++;
num[a[i]].push_back(i);
}
for(int i=0;i<m;i++){
cnt[0][a[i]]++;
}
for(int i=1;(i+m-1)<n;i++){
for(int j=1;j<=k;j++){
cnt[i][j] = cnt[i-1][j];
}
cnt[i][a[i-1]]--;
cnt[i][a[i+m-1]]++;
}
int tot = 0;
for(int i=0;(i+m-1)<n;){
bool ok = 1;
int bef = 0;
Hash[0] = -1;
//cout<<i<<"! "<<endl;
memset(Hash, -1, sizeof(Hash));
for(int j=1;j<=k;j++){
if(mo[j]>0){
//cout<<num[j].size()<<endl;
Hash[j] = x[num[j][0]+i];
if(Hash[j]>Hash[bef]){
bef = j;
}else{
ok = 0;
break;
}
}
}
if(!ok){
i++;
continue;
}
for(int j=1;j<=k;j++){
if(!mo[j])continue;
for(int l=1;l<mo[j];l++){
if(x[i+num[j][l]] != Hash[j]){
ok = 0;
break;
}
}
if(!ok)break;
}
if(ok){
tot ++;
i = i+m-1;
}
i++;
}
printf("%d\n",tot);
}
return 0;
}