问题:
一个序列为不无聊序列,则必须满足划分任意长度的连续子序列(包括其本身),其中至少存在一个元素仅仅出现一次,即至少有一个元素不重复
如:
12321 ——不无聊序列,任意子序列都有独特元素
123321 ——无聊序列,子序列“33”中没有独特元素
思想:
采用分治法求解,如果要成为一个不无聊序列,就必须至少有一个独特(与序列中任何数都不重复)的数,找到这个数并且进行递归
步骤:
- (1)定义prepos和nextpos两个数组,记录了序列中每个数前一次出现和后一次出现的位置
- (2)从序列两边依次开始遍历,通过上面两个数组找寻那个独特的数
- (3)无论是从序列左边还是序列右边找到独特的数,都以该数位置为界限,将该数左边和该数右边递归,判断这两边是否为不无聊序列,并返回bool值
- (4)若这两边均为不无聊序列,则整个序列为不无聊序列
时间复杂度:
O(nlogn)
代码(C++):
#include <iostream>
#include <map>
using namespace std;
//prepos[i]表示序列中,位于第i个元素之前,且与第i个元素相同的元素的下标是prepos[i]
//nextpos[i]表示序列中,位于第i个元素之后,且与第i个元素相同的元素的下标是nextpos[i]
int prepos[10000],nextpos[10000];
int n; //数组大小
int arr[10000]; //定义数组
map<int,int>mp; //记录每个数字出现的下标
void find(){
//赋值prepos数组
for(int i=0;i<n;i++){
int num=arr[i];
if(mp.count(num)==0){ //判断map中有没有出现该数字
prepos[i]=-1; //没出现过就赋值-1
}else{
prepos[i]=mp[num]; //出现过就赋值前一次出现时所在的索引位置
}
mp[num]=i; //更新数字对应的索引
}
mp.clear(); //清空map
//赋值nextpos数组
for(int i=n-1;i>=0;i--){
int num=arr[i];
if(mp.count(num)==0){ //判断map中有没有出现该数字
nextpos[i]=n+1; //没出现过就赋值n+1
}else{
nextpos[i]=mp[num]; //出现过就赋值前一次出现时所在的索引位置
}
mp[num]=i; //更新数字对应的索引
}
mp.clear();
}
bool judge(int left,int right){
if(left>=right) return true;
int i=left,j=right; //从序列两端开始遍历
while(i<=j){ //两端指针i,j未相遇
if(prepos[i]<left&&nextpos[i]>right){ //若序列左边存在独特(不重复)的数,则进行递归
return judge(left,i-1)&&judge(i+1,right);
}
i++;
if(prepos[j]<left&&nextpos[j]>right){ //若序列右边存在独特(不重复)的数,则进行递归
return judge(left,j-1)&&judge(j+1,right);
}
j--;
}
return false;
}
int main(){
cout<<"input seqSize: ";
cin>>n;
cout<<"input seqNum: ";
for(int i=0;i<n;i++){
cin>>arr[i];
}
cout<<"the sequence is: "<<endl;
for(int i=0;i<n;i++){
cout<<arr[i]<<"\t";
}
cout<<endl;
//找到prepos和nextpos两个数组的值
find();
//判断是否为不无聊数组
if(judge(0,n)){
cout<<"it is a non-boring sequence!";
}
else{
cout<<"it is a boring sequence!";
}
return 0;
}
运行结果: