题意
一个序列为不无聊序列,则必须满足划分任意长度的连续子序列(包括其本身),其中至少存在一个元素仅仅出现一次,即至少有一个元素不重复。
例子:
123321 ——无聊序列,子序列33中没有独特元素。
12321 ——不无聊序列,任意子序列都有独特元素。
思路
- 划分:根据当前的元素进行划分(与快速排序的partition划分主元类似),不断累加或累减left和right,从而不断缩小查找的范围
- 解决:判断在left~right的范围内是否有重复元素
- 合并:以布尔值的形式返回
递归体:判断在left~right所构成的区间内,访问prepos[]和nextpos[],看是否存在与当前元素相同的元素,没有则递归进行判断
递归出口:当左指针left大于右指针right时为递归出口
false:无聊序列
true:不无聊序列
代码
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1e5 + 5;
//prepos[i]表示序列中,位于第i个元素之前,且与第i个元素相同的元素的下标是prepos[i]
//nextpos[i]表示序列中,位于第i个元素之后,且与第i个元素相同的元素的下标是nextpos[i]
int prepos[MAX], nextpos[MAX];
int n;
int arr[MAX];
map<int, int> mp; //记录数字出现的下标
void init()
{
for(int i = 0; i < n; ++i)
{
int num = arr[i];
if(mp.count(num) == 0)
prepos[i] = -1;
else
prepos[i] = mp[num];
mp[num] = i;//存放的是索引下标
}
mp.clear();
for(int i = n - 1; i >= 0; --i)
{
int num = arr[i];
if(mp.count(num) == 0)
nextpos[i] = n + 1;
else
nextpos[i] = mp[num];
mp[num] = i;
}
}
bool solve(int left, int right)
{
if(left >= right)
return true;
int i = left, j = right - 1;//从两端遍历
while(i <= j)//直到两指针相遇
{
//判断在left~right的区间内,是否存在与元素i相等的元素
if(prepos[i] < left && nextpos[i] > right)//不存在则进行递归
return solve(left, i - 1) && solve(i + 1, right);
++i;
//判断在left~right的区间内,是否存在与元素j相等的元素
if(prepos[j] < left && nextpos[j] > right)
return solve(left, j -1) && solve(j + 1, right);
--j;
}
return false;
}
int main(int argc, char *argv[]) {
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf("%d", &arr[i]);
}
init();
if(solve(0, n))
printf("non-boring sequence\n");
else
printf("boring sequence\n");
return 0;
}