传送:
https://cn.vjudge.net/problem/Kattis-wiknow
题意:
在字符串中找形如ABAB的子序列,要求AB字典序最小。
思路:
这道题最重要的性质:如果存在形如ABAB的子序列,那么一定可以找到一个A1B1A2B2,满足A1A2之间没有其它A,B1B2之间没有其它B。
先预处理每个字符右边一个的位置。扫描一遍字符串,把每个字符右边一个加入线段树,同时枚举B1,在B1,B2之间区间最小查询,这个最小值一定在B1左边出现过,因为更新线段树时是加入每个字符右边的。
code:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef pair<int,int> P; 4 const int N=400000+10; 5 const int INF=0x3f3f3f3f; 6 int dat[4*N]; 7 int a[N],nex[N],r[N],n; 8 void update(int pos,int k,int l,int r) 9 { 10 if(r-l==1){dat[k]=a[pos];return;} 11 int m=(l+r)>>1; 12 if(pos<m)update(pos,k<<1,l,m); 13 else update(pos,(k<<1)|1,m,r); 14 dat[k]=min(dat[k<<1],dat[(k<<1)|1]); 15 } 16 int query(int a,int b,int k,int l,int r) 17 { 18 if(l==r)return INF; 19 if(a<=l&&b>=r)return dat[k]; 20 int m=(l+r)>>1,mi=INF; 21 if(a<m)mi=min(mi,query(a,b,k<<1,l,m)); 22 if(b>m)mi=min(mi,query(a,b,(k<<1)|1,m,r)); 23 return mi; 24 } 25 int main() 26 { 27 memset(dat,0x3f,sizeof(dat)); 28 scanf("%d",&n); 29 for(int i=1;i<=n;++i) 30 scanf("%d",a+i); 31 for(int i=n;i>=1;--i)//处理每个字符右边一个的位置 32 { 33 nex[i]=r[a[i]]; 34 r[a[i]]=i; 35 } 36 P ans(INF,INF); 37 for(int i=1;i<=n;++i) 38 { 39 if(!nex[i])continue; 40 int v=query(i+1,nex[i],1,1,n+1);//在当前位置与下一个位置之间查询最小值 41 ans=min(ans,make_pair(v,a[i])); 42 update(nex[i],1,1,n+1);//把当前字符下一个更新进线段树 43 } 44 if(ans.first==INF||ans.second==INF) 45 puts("-1"); 46 else printf("%d %d\n",ans.first,ans.second); 47 return 0; 48 }