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 */