题意:
就是用两长度为n,m的数组,给定一个p,让你从第一个数组下标为1开始往后寻找满足起始位置p,数列Ap,Ap+1.。。Ap+(n-1)p,使得新数组中数字出现数目与第二个数组相同,打印出种类数目和起始位置
题解:
一开始因为看到是1e9的数据,用数组保存情况肯定爆炸,就陷入了沉思,后来ljw学长在群里发了题解,一开始看学长的博客没太看懂,百度了一下。。。后来感觉理解了那个博主的思路,然后就自己就写了一遍,这题的思路就是利用数据,工具就是stl,之前没用过map不知道map那么好用,就是我们在暴力从一个起点开始算的时候,当他的长度达到了m,我们可以去掉数组的第一个元素,加上数组的最后一个元素的后面的元素(在不超出n的范围内),并将遍历所有数组元素打上vis标记,下次遍历时标记了就直接跳掉,就省去了很多时间
代码:
#include<stdio.h>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<map>
using namespace std;
const int MAX=2e5+5;
int a[MAX];
int mul[MAX];
int vis[MAX];
map<int,int>M;//直接开数组,因为数据范围为1e9,会炸,所以只能用键值对
int main()
{
int i,j,n,m,p,x;
scanf("%d%d%d",&n,&m,&p);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=0;i<m;i++)
{
scanf("%d",&x);
M[x]++;//记录数组情况
}
int ans=0,q;
if((m-1)*p+1>n)//检查合法性
{
printf("0\n");
return 0;
}
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)//从数组第一个开始遍历
{
if(vis[i])//优化
continue;
queue<int>q;
map<int,int>TM;//用于保存当前状态下的数组情况
TM.clear();
for(j=i;j<=n;j+=p)
{
q.push(j);
vis[j]=1;//标记一下避免之后重复取耗时间
TM[a[j]]++;
if(q.size()>m)//判断是否达到了长度
{
x=q.front();
q.pop();
TM[a[x]]--;
if(TM[a[x]]==0)//很重要,不擦掉会出事
TM.erase(a[x]);
}
if(TM==M)//巧用stl
{
mul[ans]=q.front();
ans++;
}
}
}
sort(mul,mul+ans);//因为放入的时候不是按下标顺序,要从小到大排序
printf("%d\n",ans);
for(i=0;i<ans;i++)
{
printf("%d",mul[i]);
if(i!=ans-1)
printf(" ");
else
printf("\n");
}
return 0;
}