欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

UVa1608 Non-boring sequences(尺取+分治)

程序员文章站 2022-03-04 21:09:58
...

题目链接
UVa1608 Non-boring sequences(尺取+分治)
1.题目大意:如果一个序列的任意连续子序列中至少有一个只出现一次的元素,则称这个序列是不无聊的。判断输入的序列是否为不无聊的

2.首先想到整体思路:在序列[l,r]中如果能找到一个只出现一次的元素,设其位置为p,那么只需要继续检查序列[l,p-1]和[p+1,r]。不难证明这个结论是正确的:首先满足大区间,接着在不考虑满足大区间的一个只出现一次的元素,如果两个子区间能找到满足的元素那么就继续分治考虑更小的区间,直到区间长度小于等于1。但是如何某个元素在这段序列是唯一?事先算出每个元素左边和右边最近的元素下标保存,设用L和R两数组保存,那么判断时只需要判断元素的L和R是否超过该区间就行。至于具体如何保存,使用map不断从左到右(从右到左)更新每个元素对应下标即可。还有一个注意点,L初始化为0,R初始化为INF

3.本题的时间复杂度的计算很经典,首先如果从右向左判断每个数是否唯一的话,最坏的话唯一的数都在最右边,那么时间复杂度为O(n2),从右向左同理。如果设置两个指针,分别从左和右开始向中间找,最坏的情况下每次都在中间找到,这样的话T(n)=2*T(n-1)+O(n),最坏的时间复杂度O(n*logn),实际上中途相遇法也是尺取的一种,算是双指针,和回文串的判断思想一致

4.需要注意使用memset初始化数组会超时,因此在两次反向遍历求L和R时也能更新为0或INF

#include <iostream>
#include <cstring>
#include <unordered_map>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=2e5+10;
int a[maxn],L[maxn],R[maxn];
unordered_map<int,int> mp;

bool solve(int l,int r){
    if(l>=r) return true;
    for(int i=l,j=r;i<=j;i++,j--){
        if(L[i]<l && R[i]>r) return solve(l,i-1) && solve(i+1,r);
        if(L[j]<l && R[j]>r) return solve(l,j-1) && solve(j+1,r);
    }
    return false;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t,n;
    cin>>t;
    while(t--){
        cin>>n;
        mp.clear();
        //memset(L,0,sizeof L);
        //memset(R,0x3f,sizeof R);
        for(int i=1;i<=n;i++){
            cin>>a[i];
            if(!mp[a[i]]) L[i]=0;
            else L[i]=mp[a[i]];
            mp[a[i]]=i;
        }
        mp.clear();
        for(int i=n;i>=1;i--){
            if(!mp[a[i]]) R[i]=INF;
            else R[i]=mp[a[i]];
            mp[a[i]]=i;
        }
        if(solve(1,n)) cout<<"non-boring"<<endl;
        else cout<<"boring"<<endl;
    }
    return 0;
}