卧薪尝胆,厚积薄发。
重复段的删除
Date: Thu Sep 27 15:36:32 CST 2018 In Category: NoCategory

Description:

如果有一个长度为 $2x$ 的串前半部分和后半部分完全相同,则称他为重复串,从小到大枚举所有的重复串,从左往右依次删掉所有重复串的左边所有字符和串的左半部分,问最后剩下的字符。
$1\leqslant n \leqslant 10^5$ 保证每个字符出现不超过 $10$ 次。

Solution:

剩下的一定是一个后缀(因为每次会消掉一个前缀),每个字符出现不超过 $10$ 次这样奇怪的数据范围一定是可以做些什么的,对于每个长度为 $2x$ 重复串,一定连续出现了 $x$ 个位置差 $x$ 的相同字符串对,所以离散化开个桶记录每个字符的所有出现位置,然后 $O(n)\times C_{10}^2$ 的枚举所有相同字符串对把它们拿出来按照差为第一关键字,位置为第二关键字从小到大排序,记一个 $cur$ 表示当前消到了什么位置,那么判断一下还没有被消过的最短的长度能不能凑成一个重复串,如果能,就更新 $cur$ 。
其实可以枚举相同字符串对然后hash判两部分是否相同。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<cstring>
using namespace std;
int n;
#define MAXN 100010
int a[MAXN];
int b[MAXN],tot = 0,s[MAXN];
map<int,int> fr;
vector<int> pos[MAXN];
struct line
{
int l,r;
}l[MAXN * 50];
int cnt = 0;
bool cmp_len(line a,line b){return (a.r - a.l == b.r - b.l ? a.r < b.r : a.r - a.l < b.r - b.l);}
int res[MAXN * 50];
int main()
{
freopen("dor.in","r",stdin);
freopen("dor.out","w",stdout);
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= n;++i)b[i] = a[i];
sort(b + 1,b + 1 + n);
for(int i = 1;i <= n;++i)
if(i == 1 || b[i] != b[i - 1])
fr[b[i]] = ++tot;
for(int i = 1;i <= n;++i)s[i] = fr[a[i]];
for(int i = 1;i <= n;++i)
{
for(vector<int>::iterator it = pos[s[i]].begin();it != pos[s[i]].end();++it)
{
l[++cnt] = (line){*it,i};
}
pos[s[i]].push_back(i);
}
sort(l + 1,l + 1 + cnt,cmp_len);
int cur = 0;
for(int i = 1;i <= cnt;++i)
{
res[i] = 1;
if(l[i].l <= cur)continue;
if(i != 1 && l[i].r - l[i].l == l[i - 1].r - l[i - 1].l && l[i].r - 1 == l[i - 1].r)res[i] += res[i - 1];
if(res[i] >= l[i].r - l[i].l)cur = l[i].l;
}
cout << n - cur << endl;
for(int i = cur + 1;i <= n;++i)printf("%d ",a[i]);puts("");
fclose(stdin);
fclose(stdout);
return 0;
}
In tag: 玄学
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡