https://ac.nowcoder.com/acm/contest/5671/K

题意:

一个序列被称为k-bag,当且仅当它以一些(可能是一个)1~k的排列时。例如,1,2,3,2,1,3,3,2,1是有效的3-bag序列。部分k-bag序列是指k-bag的连续子序列。

给出长度为n的序列,判断该序列是否为部分k-bag序列。

思路:

用一个b数组代表以i结尾的前k个字符是否有重复的,(下标小于k的前面的数字默认不同)没有重复为1,有重复数字为0;

从后往前找第一个重复数字的位置为top,从这个位置扩k位,如果top+k-1>n,那么 (n , top+k-1)之间的b数组的值为0;

下标用i表示,i%k的位置代表从 i%k 这个位置开始后面是完整的,如果b[i%k]=0,那么就不能从这个位置开始,如果这样的位置大于等于k,代表不能从前面k个数开始,就不合法。

所以:下标%k的位置为0的个数(相同位置不能重复计算)大于等于k,则不合法。

 

 

 

 

 当n<k(k很大时,不能往后补位),只会是一个不完整的部分或者两个不完整的部分,则从后往前找到第一个重复数字的位置,判断前面的数字是否重复。

代码:

#include <bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
typedef long long ll;
const int inf=0x3f3f3f3f;
const long long INF=0x3f3f3f3f3f3f3f3f;
const int MAXN=1e6+5;
const double eps=-1e8;
typedef pair<int,int>pii;
int a[MAXN],b[MAXN];
int vis[MAXN];
unordered_map<int,int>mp;//map会超时
int main()
{

    int T;
    scanf("%d",&T);
    while(T--)
    {
        mp.clear();
        int n,k;
        scanf("%d%d",&n,&k);
        int flag=0;

        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>k)flag=1;

        }
        for(int i=0;i<=2*n+2;i++)b[i]=1,vis[i]=0;

        if(flag)
        {
            printf("NO\n");
            continue;
        }
        int cnt=0;
        flag=0;
        for(int i=1,j=1;i<=n;i++)//判断以i结尾的前k个数字是否重复
        {
            if(mp[a[i]]==0)cnt++;
            mp[a[i]]++;
            if(i>k)
            {
                if(mp[a[i-k]]==1)cnt--;
                mp[a[i-k]]--;
            }
            if(cnt!=k)b[i]=0;
            if(mp[a[i]]==2)flag=1;
            if(flag==0)b[i]=1;
        }
        mp.clear();

        int top=-1;
        for(int i=n;i>=1;i--)//从后往前第一个重复数字的位置
        {
            mp[a[i]]++;
            if(mp[a[i]]==2)
            {
                top=i;
                break;
            }
        }
        if(n>k)
        {
            cnt=0;
            if(top!=-1)
            {
                for(int i=n+1;i<top+k;i++)
                {
                    b[i]=0;
                }
            }

            for(int i=1;i<=2*n+1;i++)
            {
                if(b[i]==0&&vis[i%k]==0)
                {
                    cnt++;
                    vis[i%k]=1;
                }
            }
            if(cnt<k)printf("YES\n");
            else printf("NO\n");
        }
        else  //n<k 后一段无重复数字,判断前半段是否有重复数字
        {
            flag=0;
            mp.clear();
            for(int i=1;i<=top;i++)
            {
                mp[a[i]]++;
                if(mp[a[i]]>1){flag=1;break;}
            }
            if(flag)printf("NO\n");
            else printf("YES\n");
        }
    }

    return 0;
}

/*
10
4 6
1 2 3 3
4 6
1 6 6 1
7 8
1 2 3 3 4 5 6
7 8
1 2 3 3 2 1 1
7 8
1 2 3 3 2 1 4
7 4
4 4 4 3 2 1 4

*/
View Code

 

posted on 2020-07-29 15:40  MZRONG  阅读(301)  评论(0编辑  收藏  举报