题目链接
题意:
定义k-bag为若干长度为k的全排列组成的串,问题目给出的串是否可能为一个k-bag的子串。
贪心思路
设定当前全排列的左右指针,从左向右加点,大致情况分以下几种:
1.尾指针遇到可加入的数,直接加入。
2.当前段满足全排列,进入下一段匹配。
3.尾指针遇到重复的数,此时无法继续向右添加,考虑向左边加数,同时将之前匹配成功的每段全排列向左移动。考虑以下两种情况:
3.1.头指针是否能前移?若左边需添加的数与当前段中的数有重复,则说明该数与当前段重复的数必不在同一段中,则让尾指针回退,直到可添加为止。
3.2.之前匹配成功的每一段是否能够前移?每一段前移会失去一个数同时得到一个数,那么这两个相距k的数必须相等才能前移,也就是说若位置m要前移,位置m-k,m-2k…都需相等。预处理即可。
贪心代码
#include<bits/stdc++.h>
#define ll long long
#define LL long long
#define PB push_back
#define MP make_pair
using namespace std;
const int maxn=5e5+100;
const ll inf=1e18+10;
int a[maxn],back[maxn],vis[maxn];
map<int,int>mp;
int main()
{
int t,n,k;
scanf("%d",&t);
while(t--){
mp.clear();
scanf("%d %d",&n,&k);
int okk=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>k)okk=0;
back[i]=vis[i]=0;
}
if(!okk){
puts("NO");
continue;
}
int st=1,ed=1,ok=1;
back
for(int i=1;i<=n&&i<=k;i++){
back[i]=1;
}
for(int i=k+1;i<=n;i++){
if(back[i-k]&&a[i]==a[i-k])
back[i]=1;
}
mp.clear();
for(int i=1;i<=n;i++){
if(mp[a[i]]){
st=ed=i;
break;
}
mp[a[i]]++;
}
int sz=0;
mp.clear();
ok=1;
while(ed<=n){
if(!ok)break;
if(mp[a[ed]]){
ed--;
while(sz<k){
while(mp[a[st-1]]){
if(vis[ed])goto ed;
vis[ed]=1;
mp[a[ed--]]--;
sz--;
}
if(st-1>0&&back[st-1]){
mp[a[st-1]]++;
sz++;
st--;
}
else{
ed:
ok=0;
break;
}
}
if(sz<k){
ok=0;
break;
}
}
else{
mp[a[ed]]++;
sz++;
}
ed++;
if(sz==k){
mp.clear();
sz=0;
st=ed;
}
}
if(ok)puts("YES");
else puts("NO");
}
}
枚举思路
首先用滑动窗口计算长度为k的连续区间是否为全排列,枚举左右分割点判断即可。
枚举代码
#include<bits/stdc++.h>
#define ll long long
#define LL long long
#define PB push_back
#define MP make_pair
using namespace std;
const int maxn=5e5+100;
const ll inf=1e18+10;
unordered_map<int,int>mp;
int n,k,a[maxn],f[maxn],pre[maxn],lst[maxn];
inline void init(){
mp.clear();
int cnt=0;
for(int i=1;i<=n;i++){
if(i>k){
mp[a[i-k]]--;
if(!mp[a[i-k]])cnt--;
}
if(!mp[a[i]])cnt++;
mp[a[i]]++;
if(cnt==k)f[i]=1;
}
mp.clear();
for(int i=1;i<=n;i++){
if(!mp[a[i]])pre[i]=1;
else break;
mp[a[i]]++;
}
mp.clear();
for(int i=n;i>=1;i--){
if(!mp[a[i]])lst[i]=1;
else break;
mp[a[i]]++;
}
}
inline void slove(){
scanf("%d%d",&n,&k);
int p1=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>k)p1=0;
f[i]=pre[i]=lst[i]=0;
}
if(!p1){
puts("NO");
return;
}
init();
int num=(n-1)%k+1,ok=0;
for(int i=0;i<=num;i++){
int l=0,r=0;
if(i==0||pre[i])l=1;
if(n-num+1+i==n+1||lst[n-num+1+i])r=1;
if(l&&r){
int okk=1;
for(int j=i+k;j<n-num+1+i;j+=k){
if(!f[j])okk=0;
}
if(okk){
ok=1;
break;
}
}
}
for(int i=1;i<n;i++){
if(pre[i]&&lst[i+1])ok=1;
}
if(!ok){
num+=k;
if(num<=n){
for(int i=0;i<=num;i++){
int l=0,r=0;
if(i==0||pre[i])l=1;
if(n-num+1+i==n+1||lst[n-num+1+i])r=1;
if(l&&r){
int okk=1;
for(int j=i+k;j<n-num+1+i;j+=k){
if(!f[j])okk=0;
}
if(okk){
ok=1;
break;
}
}
}
}
}
puts(ok?"YES":"NO");
}
int main(){
int t;
scanf("%d",&t);
while(t--){
slove();
}
}