题目描述
输入描述:
输出描述:
示例1
输入
6 3
1 4 2 8 5 7
2 3 3
输出
2
说明
题目大意
给定2个数列a和b,其中alen>=blen,求b有多少种可能与a的长度为blen的子串匹配,匹配的条件是对于任意的i,满足ai>=bi,则匹配。
分析
一个错的思路
许多人拿到这题,就会想到魔改KMP,嗯我也是这么WA过来的。随手就这么一段代码,于是喜提WA一枚(其实不止,大概有5枚)。
那么问题来了,KMP到底为什么不能用呢。
看官可以试试下面这组数据:
a:1 1 2 2 1 2 1 1 2 1 1
b:2 1
如果你的答案是3(正确的是4才对),那么就应该知道为什么了。下面不再赘述。
一个对的思路(假)
DP!对就是DP,设dp[i][j]表示对于第i位的ai,b在第j位与之匹配,且之后的均已匹配,则dp[i][j]=1,否则=0。
即dp[i][j]表示ai—i+blen-j与bj—j+blen匹配。
则有dp转移式: dp[i][j]=dp[i-1][j-1]&(a[i]>=b[j]);
由此可知,答案存在dp[i][1]中,最后累加下即可。
但是,算下复杂度又发现不对,O(nm)啊,这不炸了吗?居然有dp会炸,那怎么办呢?
一个真的对的思路
其实上面说的已经不假了,就是用dp做的,观察这个dp,发现它只存了01两种值,此时我们可以用到一个STL:
bitset
bitset是一个01数组,可以像整数那样位运算,虽然还是慢,但是可以加速到原来的1/32,(机子好的老板64位则可以1/64),刚好卡过时间(从未见过如此厚颜无耻之操作)。
接下来是bitset的一些用法:
bitset<40010> bt;//定义一个长为40010的名为bt的bitset
bitset<40010> bt[MAXN]//如同vector一样,也可以定义成数组的bitset
bt.set()//把bitset bt的所有值赋为1
bt.reset()//赋为0
bt.set(pos)//把pos位的赋为1,reset同
bt.count()//数有几个1
OK,有了bitset这一神器,可以把dp转成bitset了。
dp[i][j]就是bti[j]
dp[i-1][j-1]就是bti-1>>1,(这里值得注意的是,bitset的存的方向是反的)
所以bti=((bti>>1)|(1<<m))&(a[i]>=b[j])
这下麻烦了,我不知道j了啊。所以a[i]>=b[j]要预处理,再用二分查找函数快速找到。
预处理这一部分建议看下官方题解,写的蛮详细的。
好了话不多说,上DM。
代码
#include<bits/stdc++.h>
#define ll long long
#define inf 1<<30
using namespace std;
const int MAXN=150010;
const int MAXM=40010;
struct node{
int v,p;
}a[MAXN],b[MAXM];
bool cmp(node x,node y){return x.v<y.v;}
bitset<40010> s[MAXM],cur,I;
int main()
{
int n,m,ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i].v);
for(int i=1;i<=m;i++) scanf("%d",&b[i].v),b[i].p=i;
sort(b+1,b+1+m,cmp);
for(int i=1;i<=m;i++){
s[i]=s[i-1];
s[i].set(b[i].p);
}//预处理
I.set(1);//好像没什么用,各位看官帮蒟蒻想一下
cur.set();
for(int i=1;i<=n;i++){
int pos=upper_bound(b+1,b+1+m,a[i],cmp)-b-1;//用二分查找函数
cur=(((cur<<1)|I)&s[pos]);
if(i>=m&&cur[m]) ans++;
}
printf("%d\n",ans);
}//cur即dp,官方用了cur,看题解时避免麻烦就用了cur了
END
蒟蒻写得不好,dalao见谅。
这个大佬的题解好,可以看下。